Влад : другие произведения.

Исходные тексты скриптов задач "siTop…"

"Самиздат": [Регистрация] [Найти] [Рейтинги] [Обсуждения] [Новинки] [Обзоры] [Помощь|Техвопросы]
Ссылки:


 Ваша оценка:
  • Аннотация:
    По состоянию на 9 сентября 2023 года.

См. также:
? Как узнать популярность произведения в "Самиздате" ("где/что смотреть" - основное, вкратце).
~ Рейтинг siTopTexts "Произведения" ("Самиздат", самое популярное за 30 дней).
+ Рейтинг siTopAuthors "Авторы" ("Самиздат" намедни, приложение к siTopTexts).
* История-Р, "прикопанное" прошлое (ссылки на подборки старых рейтингов по полугодиям).
Disclaimer: Я не несу никакой ответственности за последствия касаемо. Далеко не факт, что у вас получится то, на что вы рассчитываете, если станете использовать нижеприведённое. Пользуйтесь оным исключительно на свой страх и риск. ;-|
… В любом случае, на вопросы касаемо — отвечу, если смогу…
  /\    /\  
 ·--·--·--· 
     \/     
     /\     
    ·--·    
Исходные тексты скриптов задач "siTop…"
(от: 2023.09.09)
    NB. В публикации приведен комплект оригинальных исходных текстов скриптов, не подвергавшихся какой-либо предварительной правке (за исключением преобразования: «OEM→ANSI» кириллицы в скриптах CMD). Этот комплект работает в ОС WindowsXP-x86-RUS+SP3+SRK, использует некоторые бесплатные утилиты (порт из UNIX) и ориентирован на полуавтоматическое применение в моей рабочей среде. Влад.

Перечень задействованных скриптов:

  1. Задача: «Разделы "Самиздата"»
    1. Задание: "0form" — выявление скрытых разделов /"потеряшки"/
    2. Задание: "1hidn" — вычленение активных "потеряшек"
    3. Задание: "aHits" — обновление локальной копии рейтинга "по количеству посетителей"

  2. Задача: «Рейтинг siTopTexts "Произведения"»
    1. Задание: "bStat" — обновление локальных копий статистик топовых разделов, сборка шорт-листа к siTopTexts
    2. Задание: "cChkH" — контроль даты "Размещен" произведений в шорт-листе siTopTexts
    3. Задание: "dText" — сбор сведений о произведениях (объём, оценки, отклики, …)
    4. Задание: "fHtml" — оформление таблиц рейтинга siTopTexts

  3. Задача: «Рейтинг siTopAuthors "Авторы"»
    1. Задание: "gAuth" — составление и наполнение шорт-листа siTopAuthors
    2. Задание: "iNetR" — оформление таблиц siTopAuthors

  4. Задача: «Оценка фазы обновления статистик разделов "Самиздата"»
    1. Задание: "zSchk" — обнаружение факта "«сегодня» обновлено"

  5. Универсальный скрипт "качалка" для скачивания страниц "Самиздата"

    Имя файла скрипта: "0form.awk" V-текст скрипта-V
- - - - - - - - -
BEGIN{                              #  rAddF.awk - вычленение a_Id отсутствующих в aHits разделов
#  2023.07.08, nvv, creation на входе - страницы произведений по 18 формам СИ
#  2023.07.13, nvv, turning
#  на выходе - список id разделов, ОТСУТСТВУЮЩИХ в aHits
#   test=1
  errS="no inPut files"
  jDat=strftime("%Y.%m.%d")
  if(ARGV[1]=="")exit                             #  защита от случайного пуска
  for(str2 in ENVIRON){                     #  ! регистр букв в имени переменной может быть любой
    if(tolower(str2)=="fhit")fHit=ENVIRON[str2]   #  опорный aHits список
    if(tolower(str2)=="jlst")jLst=ENVIRON[str2]   #  место для итогового списка
  }
  if(fHit==""){                                   #  если вызов НЕ из CMD, aHits ищем сами
    if(test){
    str0="U:\\Jobs\\_siTop.#"
    while((getline line<str0)>0){       #  ищем последнюю запись успешного скачивания этого списка
# ;1A hits 5:xNam 457:xNum 104555:xPas 2023.06.15:hDat 505501:hMax 1051:hPag 1111:dnLd 0.08~ratW 2023.06.16:done 05:22:08~wrks 14:47
      if(match(line,/^;1A .+ 20([\.0-9]{8}):hDat /,arr0))fHit=arr0[1]
    }
    close(str0)
    if(fHit==""){
      errS="] NO *.aHits found in "str0
      exit
    }
    jLst="_lost.addF"
    fHit="U:\\Jobs\\20"fHit".aHits"               #  строим полное имя нужного списка
    }else{
      errS="NO fHit envVar"
      exit
    }
  }
# # 201661   21  846    3 k/kadawr "Кадавр" "no•comments"
# # ] hitP numP sizK sizN id_P     "namP"   "moto"
# #      1    2    3    4  5        6        7
  while((getline line<fHit)>0){                   #  fHit: like 2020.03.01.aHits
    split(line,aj)
    if(match(aj[1],/^[0-9]+$/)){                  #  в опоре "потеряшки" неуместны
      if(aj[2])aHit[ aj[5]]=line  #  aj[1] == 201661 aj[5] == k/kadawr p_id
    }else                       aHre[++nHre]=line
    j0++
  }
  close(fHit)                                     #  всё/не_всё, - старый список тогось
  if(99999-j0>0){
    errS=j0" LOW aHits.recs"
    exit
  }
  errS=""
}

{
  if(FNR==1){
    pagF++                                        #  учитываем страницы форм
    if(pagF%10){printf(".")}else{printf(":")}     #  оживляж...
    if(!(pagF%100))printf("%5s\n",pagF)
  }
  if(match($0,/<DL><DT><li>.*<a href=\/([^>]+)><font color=#555555>([^<]*)</,aj)){
    aFrm[aj[1]]++                                 #  учитываем id_P и тексты в них
  }
}

END{
  if(errS!=""){                                   #  проблем-с...
    print("] "errS)
    exit -1
  }
  printf("%5s\n",pagF)
  txtF=0
  autF=0
  for(j0 in aFrm){
    if(!(j0 in aHit))aNot[++nNot]=sprintf("%5s %s",aFrm[j0],j0)
    txtF=txtF+aFrm[j0]
    autF++
  }
  j3="; 18:form "pagF":pags "txtF":txts "autF":auth "nNot":hidn "jDat":done"
  asort(aNot)
  print(j3) > jLst
  print(aHre[nHre])                     > jLst
  print(";txTot id_P")                  > jLst
  for(j0=nNot;j0>0;j0--)print(aNot[j0]) > jLst
  print(j3)
}

    Имя файла скрипта: "0form.cmd" V-текст скрипта-V
- - - - - - - - -
@if not defined nJob @echo off
:: rAddF.cmd - составление /по спискам форм/ списка _lost.addF разделов, отсутствующих в aHits- 
:: 0. для старта нужнен список aHits (хиты)
:: 1. обновляется страницы списков "формы" 01.0001.form.htm .. 18.NNNN.form.htm

::  основной и резервный URL Самиздата (желательно работать с резервом)
echo %date% %time:~0,8% %~nx0 Begs
set siUrl=samlib.ru
if exist \siUrl.budclub set siUrl=budclub.ru

:: 2023.06.20, nvv, сознано на основе aHits.cmd от 2023.05.30
:: 2023.07.10, nvv, turning...

:: основные сеты и прочая подготовка к работе...
set nJob=addF
set jDat=%date%
set errInf=
::  wrks - общее время работы процесса (пуск общего таймера)
for /F %%A in ('gawk.exe "BEGIN{print(systime());exit}"') do set wrks=%%A
::  aScr - скрипт вычленения потеряшек из страниц списков произведений по формам
set aScr=%~dpn0.awk
if NOT exist %aScr% set errInf=NO %aScr% found & goto nExit
::  wDir - общее рабочее место
set wDir=%~d0\Jobs
::  jDir - место для оперативных файлов
set jDir=%wDir%\_%nJob%
::  zDir - общий подкаталог для посуточных подкаталогов с итоговыми списками
set zDir=%~d0\Stor\wOld\%jDat%
::  jLst - конечный результат процесса в формате *.mig.bStat
set jLst=%wDir%\_lost.%nJob%
::  jRpt - общий отчёт с итоговыми сводками процессов
set jRpt=%wDir%\_siTop.#
::  fil1 - первая страница первой формы
set fil1=%jDir%\01.0001.form.htm
::  fOld дата файла наличного рейтинга *.aHits
set fOld=
::  dnLd - счётчик скачанного ([пере]запустим "таймер" ratW)
set dnLd=0
::  wGetSiz - размер скачанного файла (default 1b минимум)
set wGetSiz=1
:: проверить/создать нужные структуры для работы (оперативные площадки)
if NOT exist %jDir%\*.* (
  mkDir %jDir%
  if errorLevel 1 set errInf=fail mkDir %jDir% & goto nExit
)
if NOT exist %zDir%\*.* (
  mkDir %zDir%
  if errorLevel 1 set errInf=fail mkDir %zDir% & goto nExit
)
:: для корректной работы rAddF.awk нужен aHits /рейтинг по хитам/
set fHit=
for %%A in (%wDir%\20??.??.??.aHits) do set fHit=%%A
if NOT defined fHit set errInf=NO *.aHits found, no job 2do & goto nExit
:: ок, работать можно, смотрим наше хозяйство
set tmp0=
:: ищем 20??.??.??.stat.add2h, /список годных "потеряшек"/ берём его полное имя и дату в нём
for %%A in (%jLst%) do set tmp0=%%~tA
:: может сегодня уже ВСЁ сделано?
if %jDat%. == %tmp0:~0,10%. echo toDay's %jLst% found, exit & goto :eof
:: сегодняшнего нет... а что есть?
if NOT exist %jDir%\*.form.htm goto chkDl
:: смотрим дату создания 01.0001.form.htm
set oDat=
for %%A in (%fil1%) do set oDat=%%~tA
:: если сегодняшняя, надо возобновить скачивание
if %jDat%. == %oDat:~0,10%. goto chkDl
:: иначе - нужно почистить место ДО того, как
set tmp0=
echo old %oDat% *.form.htm found; upDate them? [yes] == y
set /p tmp0=?[no]?
if /i NOT %tmp0%. == y. (
  echo job canceled {NO to upDate}
  exit
)
echo erasing ~7.7k old pages of 18 form...
erase /F /Q %jDir%\*.htm
if errorLevel 1 set errInf=fail erase %jDir%\*.htm &  goto nExit
:chkDl проверка наличия, скачивание недостающего; ratW - темп скачивания
for /F %%A in ('gawk.exe "BEGIN{print(systime());exit}"') do set ratW=%%A
:: собственно перебор/запуск_скачивания
echo ? dnLd %nJob% fresh pages...
for /L %%A in ( 1, 1, 18 ) do if NOT defined errInf call :gainThis %%A
if defined errInf goto nExit
:: вычислим темп скачивания ratW
if %dnLd% == 0 (set ratW=0) else (
  for /F %%A in (
    ' gawk.exe "BEGIN{print(sprintf(\"%%3.2f\",(systime()-%ratW%)/%dnLd%));exit}" '
  ) do set ratW=%%A
)
echo.
echo = %dnLd%:pages 18:forms %ratW%:ratW scaning...
:: формы у нас, соберём из них список скрытых id_P
gawk.exe -f %aScr% %jDir%\*.form.htm
if NOT %errorLevel% == 0 set errInf=%aScr% problem in %jDir%\*.form.htm & goto nExit
rem ; 18:form 7687:pags 1535762.txts 104310:auth 1948:hidn 2023.06.28:done
for /F "tokens=1-9,*" %%A in (
  ' gawk.exe "{if(match($2,/^18:form$/))print($3,$4,$5,$6);exit}" %jLst% '
) do (
  set pags=%%A
  set txts=%%B
  set auth=%%C
  set hidn=%%D
)
:: почистим, вычленив цифры
set pags=%pags:~0,-5%
set txts=%txts:~0,-5%
set auth=%auth:~0,-5%
set hidn=%hidn:~0,-5%

rem ;18:form 7689:pags 1536388:txts 104325:auth 1927:hidn 2023.07.04:done
rem ~0,-5:pags ~0,-5:auth ~0,-5:hidn

:: остановим "таймер", : преобразуем секунды в время работы (в обычном формате "чч:мм:сс")
for /F %%A in ('gawk.exe "BEGIN{print(strftime(\"%%H:%%M:%%S\",systime()-%wrks%+46800));exit}"') do set wrks=%%A

:: ;0F addF 1943:hidn 7338:FdlS 1.21~FrwS 2023.07.02:done 02:29:22~wrks 16:38 
set tmp0=;0F form ^
%pags%:pags ^
%auth%:auth ^
%hidn%:hidn ^
%ratW%~ratW ^
%jDat%:done ^
%wrks%~wrks ^
%time:~0,5%
echo %tmp0% >> %jLst%
echo %tmp0% >> %jRpt%
rem echo = Ok %hidn%.hidn %dnLd%:FdlS %ratW%~FrwS
:: скинем результаты в хранилище
copy /y /b %jLst% %zDir%\ > nul
if errorLevel 1 set errInf=fail copy %jLst% TO %zDir% & goto nExit
:: подудим где нада "готово-с!"
set itog=%jLst%

:nExit
if defined errInf if NOT defined errF echo ! %errInf%
echo %date% %time:~0,8% %~nx0 Ends
goto :eof

rem gawk.exe -f %aScr% %jDir%\*.htm
rem goto :eof

:gainThis %%Z скачивание всех страниц указанного типа
set typB=0%1
set typB=%typB:~-2%
::  wReq - общая часть адреса страниц указанного рейтинга
set wReq=%siUrl%/type/index_type_%1-
call :reqSI 1
if defined errInf goto :eof
:: выясним pMax /общее число страниц рейтинга/
set pMax=
for /F %%J in (
  ' gawk.exe "{if(match($0,/<\/td><\/tr><\/table><center><b>.+\.shtml>([0-9]+)<\/a> *<\/center>/,a)){print(a[1]);exit}}" %wFil% '
) do set pMax=%%J
if NOT defined pMax (
  set errInf=NO pMax in %wFil%
  goto :eof
)
gawk.exe "BEGIN{printf(\" next.%pMax% ones \n.\");exit}"
for /L %%J in (2,1,%pMax%) do if NOT defined errInf call :reqSI %%J
if defined errInf goto :eof
if NOT "%pNum:~-2%" == "00" echo.
rem echo.= %typB%.%pNum% pages
gawk.exe "BEGIN{printf(\"= %typB%.%pNum% pages\");exit}"
goto :eof

:reqSI %%J притаскивает type в кодировке cp1251 (win)
set wUrl=%wReq%%1.shtml
set pNum=000%1
set tmp0=   %1
::  pNum - 4-х значное число с лидирующими нулями (для красивой сортировки пронумерованных страниц рейтинга)
set pNum=%pNum:~-4%
set tmp0=%tmp0:~0,2%
::  wFil - конечный локальный файл для скачиваемого
set wFil=%jDir%\%typB%.%pNum%.form.htm
if %pNum:~-1% == 0 (set mark=%pNum:~-2,1%) else (set mark=.)
:: оживляж, если уже есть
if exist %wFil% (
  if NOT %pNum:~-1% == 0 (set mark=~)
)
:: первая странимца очередного списка по форме нужна для выяснения числа страниц этой формы
if %1 == 1 goto doDl
gawk.exe "BEGIN{printf(\"%mark%\");exit}"
if NOT "%pNum:~-2%" == "00" goto doDl
echo. = %pNum:~0,2% %time:~0,8%
:doDl
if exist %wFil% goto :eof
call wGetUrl2fil.cmd %wUrl% %wFil%
:: проблема?
if %wGetErr% == 1 set errInf=no %wUrl%
if %wGetErr% == 3 set errInf=no web{SI}
goto :eof

    Имя файла скрипта: "1hidn.awk" V-текст скрипта-V
- - - - - - - - -
BEGIN{                            #  0addS.awk scan+select *.stat.htm for addition 2 top SI stats
#  test=1
# 2023.06.26, nvv, creation на основе bStat.awk 2023.06.22
# 2023.08.15, nvv, last upDates
# ; stat 1939:allS 824:good 2023.07.19:dEnd 2023.07.23:done
# ;     datS_timS.1    datJ.2    hitP.3 id_P.4 "namP".5 "moto".6 id_F.7
# 2023.07.20_01:54:05 2023.07.19   9073 n/neon13 "Neon13" "Авторский" n~neon13
#  у ГОДНЫХ скрытых разделов:
#  - в порядке шапка,
#  + hitP >0 - хиты за 12мес.
#  + datJ == naxD - т.е. статистика обновлена к расчёту
#  id_P == n/neon13
#  ~ прочие данные носят справочный характер /для наблюдателя/
#  на выходе:
#  allS - число не нулевых файлов *.stat.htm
#  good - число годных скрытых разделов для включения в aHits
#  dEnd - граница расчёта 

  nMig=0                                          #  число годных к расчёту разделов-потеряшек
  maxD="----.--.--"                               #  верзняя граница интервала дат расчёта
  errS="NO file"
  if(ARGV[1]=="")exit                             #  защита от случайного пуска

# объявим массивыы для общего использования в подпрограммах
  split("",aSel)    #  в ячейках статистики: url,title,hits:all,12mon,mon0..monC-11,day0..day0-61
  split("",aMig)                                  #  список timeStamp's статистик
  split("",aj)                                  #  рабочий массив

  for(str2 in ENVIRON){                     #  ! регистр букв в имени переменной может быть любой
    str1=tolower(str2)                            #  Temp и temp - РАЗНЫЕ имена для gAwk...
    if(str1=="wdir")wDir=ENVIRON[str2]"\\"        #  место для всех ВЫХ.списков
  }
  if((wDir=="")&&!test){
    errS="NO wDir envVar"
    exit
  }
# массив имён месяцев для преобразования их в числа с лидирующим нулём 01..12
  mNum["Jan"]="01"                                #  .1 January . Jan. 31 . winter wint
  mNum["Feb"]="02"                                #  .2 February .Feb. 28/29
  mNum["Mar"]="03"                                #  .3 March . . Mar. 31 . spring sprn
  mNum["Apr"]="04"                                #  .4 April . . Apr. 30
  mNum["May"]="05"                                #  .5 May . . . May. 31
  mNum["Jun"]="06"                                #  .6 June . . .Jun. 30 . summer summ
  mNum["Jul"]="07"                                #  .7 July . . .Jul. 31
  mNum["Aug"]="08"                                #  .8 August . .Aug. 31
  mNum["Sep"]="09"                                #  .9 September Sep. 30 . autumn autm
  mNum["Oct"]="10"                                #  10 October . Oct. 31
  mNum["Nov"]="11"                                #  11 November .Nov. 30
  mNum["Dec"]="12"                                #  12 December .Dec. 31 . winter wint
  errS=""
  tIni=systime()
}

function mkLn() {
  if(hitP-9>0){
    aMig[++nMig]=datS"_"timS" "datJ sprintf("%7s %s %s %s %s",hitP,id_P,namP,moto,fNam)
    if(maxD<datJ)maxD=datJ
  }
}

{                                                 #  // -=- основной блок скрипта begs
  if(FNR==1){         #  секция завершения работ с одним файлом и подготовки к работе со следущим
    if(NR!=1)mkLn()                         #  оценка данных обработанной статистики, отбор годных
    step=0        #  0-владелец, 1-дата обновления, 2-id раздела, 3-разбор строк таблиц статистики
    fNam=split(FILENAME,aj,"\\")                #  NEW/NEXT входной файл as-is (с полным путём)
    fNam=split(aj[fNam],aj,".")               #  файл: чистое nam.ext (like "0~0.stat.htm")
    fNam=aj[1]                                #  fNam чистое имя (like "0~0" in "0~0.stat.htm")
    hitP=0                                        #  выясняется в stat
    id_P=substr(fNam,1,1)"/"substr(fNam,3)        #  - "" -- "" -- "" -
    namP="\"\""                                   #  - "" -- "" -- "" -
    moto="\"\""                                   #  - "" -- "" -- "" -
    inTab=0                                       #  сначала - мы НЕ "в таблице"
    numT=0                                        #  счётчик таблиц в файле статистики
    numR=0                                        #  счётчик строк в текущей таблице
    selN=0                                        #  счётчик ячеекк в строке таблицы
    fNum++
    if(fNum%100==0) print(sprintf(": %4s",fNum/100))
    else if(fNum%10)printf(".")
    else            printf(":")                   #  оживляж...
  }
  gsub(/(&nbsp;)+|\xA0+|[\0-\31]+/," ",$0) #  цепочки "&nbsp;" и прочих "пробелов" -> " " (пробел)
  if(step==0) {                                   #  ищем (для контроля) ФИО автора
# <h3>Статистика раздела &quot;<a href=./>Влад</a>&quot;:</h3>
    if (match($0,/<h3>Статистика раздела \&quot;<a href=\.\/>(.*)<\/a>\&quot;:<\/h3>/,aj)) {
      if(aj[1]==""){                            #  errS="] step."step" (NO nick in: " fNam " )"
        nextfile
      }else namP=pack(aj[1])
      step=5                                      #  проверка ОК
    }
  }else if(step==5){                              #  вычленеие даты обновления статистики
# <center><h2>Журнал &quot;Самиздат&quot;: Живопись</h2>
    if (match($0,/<center><h2>Журнал \&quot;Самиздат\&quot;: (.*)<\/h2>/,aj)) {
      moto=pack(aj[1])
      step=1                                      #  проверка ОК
    }
  }else if(step==1){                              #  вычленеие даты обновления статистики
    if(match($0,\
/<li><i>Статистика рассчитывается .+\. \(.+ (...) +([0-9]+) (..:..:..) (20[0-9][0-9])\)<\/i>/\
,aj)){
      if(length(aj[2])==1) aj[2]="0"aj[2]   #  причешем первые дни месяца "1" -> "01"
      ticS=mktime(aj[4]" "mNum[aj[1]]" "aj[2]" 12 00 00") # тики на 12 часов даты статистики
      datS=aj[4]"."mNum[aj[1]]"."aj[2]      #  дата обновления статистики
      timS=aj[3]                                #  время обновления статистики
      step++                                      #  дату статистики оптичили
    }
  }else if(step==2){            #  ищем authID (!должен быть и совпадать с префиксом имени файла)
# [<a href=http://top.mail.ru/pages?id=77427&period=0&date=2018-10-02&filter_type=0
# &filter=n/nosow_w_w&pp=20&gender=0&agegroup=0>по дням</a>]
    if (match($0,/\[<a href=http.+&filter=([0-9a-z]\/[\-0-9a-z_]+)&pp=.+>по дням<\/a>]/,aj)) {
      if(id_P!=aj[1]){  #  это ПОЛНЫЙ криминал!# errS="] step."step" ("aj[1]" != fNam:"fNam")"
        nextfile
      }
      step++                                      #  проверка ОК
    }
  }else if(step==3){                #  таблицы статистики, просмотр строк, сборка данных в ячейках
    if($0=="<!--------- Подножие ------------------------------->"){  #  разбор закончен
      step++
      nextfile
    }
    if(inTab){                      #  Мы ВНУТРИ таблицы, собираем строки таблицы с хитами текстов
      buf0=buf0" "$0                              #  gain inpLines to buf0, then check contents
      while((match(buf0,/<tr ?[^>]*>/))||(int0=index(buf0,"</tr"))){ # seek <tr .. </tr for tabRow
        if(RSTART){                               #  new/next line begs
          numR++                                  #  счётчик строк
          buf0=substr(buf0,RSTART+RLENGTH)  #  del всё до начала строки таблицы <tr>, в т.ч. и его
        }
        if(int0=index(buf0,"</tr")){              #  this line ends
          getSels(substr(buf0,1,int0-1))          #  tabl_row -> arrow:sell_values
          buf0=substr(buf0,int0+4)              #  убрали эту строку из буфера и окультуриваем...
          id_T=aSel[0]                        #  имя+расшир файла с текстом (или "./" для раздела)
          namT=aSel[1]                    #  пока фактическое (as-is) название текста в статистике
#           if(minT-aSel[3]>0)nextfile # в ЭТОЙ строке за "12мес" МЕНЬШЕ границы, дальше неинтересно
          if((numT==1)&&(numR==2)){               #  хиты по разделу (спец.обслуживание)
            hitP=aSel[3]                          #  хиты раздела за "12мес" ЕСЛИ нет в aHits
            nextfile
          }
        }
      }
      if(int0=index(buf0,"</table")){             #  this tabl ends
        buf0=substr(buf0,int0+4)                  #  update buf0
        inTab=0
      }
    }else{                                        #  ищем начало таблицы
      if(int0=index($0\
,"<table border=1 cellspacing=0 cellpadding=0><tr><td><td colspan=2><b>Итого</b></td>")){
        numT++                                    #  new table
        numR=1                #  певая строка каждой таблицы - календарь хитов статистики раздела
        buf0=substr($0,int0+80)
        if(numT==1){                              #  1-st table 1-st row -> get col-dates & so-on
          match(buf0,/<tr[^>]*>(.+)<\/tr[^>]*>/,aj) #  собственно строка с календарём
          getSels(aj[1])                        #  но самую первую такую - разберём-с
          tic0=ticS           #  и привяжем дату к нулевому (самому левому) дню посуточных данных
          while(strftime("%d",tic0)!=aSel[16])tic0=tic0-86400 #  сдвинем стат.дату к нулевому дню
          datJ=strftime("%Y.%m.%d",tic0-86400)    #  полная дата первого дня в интервале 30 дней
        }
        inTab=1                                   #  мы ВНУТРИ таблицы
        buf0=""                                   #  буфер чист!
      }
    }
  }
}                                                 #  // -=- основной блок скрипта ends

function getSels(rowLn){                #  разделение строки таблицы статистики на массив значений
  selN=0                                          #  sells count
  split("",aSel)
  aSel[0]=""
  while(match(rowLn,/<td[^>]*>/)){                #  это объявление ячейки
    selN++                  #  0.1-имя/ссылка; хиты: 2-всего, 3-12мес, 4..15-заМес, 16..77-заСутки
    rowLn=substr(rowLn,RSTART+RLENGTH)            #  выкусим объявление ячейки
    f3n2=index(rowLn,"</td")                      #  это конец ячейки
    f3s2=substr(rowLn,1,f3n2-1)               #  это сама ячейка (с возможными внутренними тегами)
    if(selN==1){                            #  в ПЕРВОЙ ячейке - название текста и ссылка на него
      match(f3s2,/<a href=([^>]+)>(.*)<\/a>/,f3a1)  #  выцепим и разложим:
      aSel[1]=f3a1[2]                             #  название текста
      aSel[0]=f3a1[1]                             #  ссылка на текст: name.shtml
    }else aSel[selN]=rmTags(f3s2) #  в ПРОЧИХ ячейках - только значение ячейки, очищенное от тегов
    rowLn=substr(rowLn,f3n2)                      #  выкусим оптиченную ячейку
  }
  if(selN==77)return                              #  row HAVE had 77 sells
  nextfile                                        #  битая таблица == битый файл
}

function rmTags(f2s1){                            #  удаление ВСЕХ тегов в литерале
  while(match(f2s1,/<\/?[a-zA-Z][1-6a-zA-Z]?[a-zA-Z]*[ \/]?[^>]*>/))
      f2s1=substr(f2s1,1,RSTART-1) substr(f2s1,RSTART+RLENGTH)
  return(f2s1)
}

function pack(f1s1) {       #  мешающие chars -> нейтральные + cut too long + закавычить результат
  f1n1=split(f1s1,f1a1)
  f1s1=f1a1[1]                                    #  обихаживание пробелов... " " -> "•"
  for(f1n2=2;f1n2<=f1n1;f1n2++)f1s1=f1s1"•"f1a1[f1n2]
  f1s2=""                                         #  двойные кавычки -> "&quot;"
  while(f1n1=index(f1s1,"\"")){f1s2=f1s2 substr(f1s1,1,f1n1-1)"&quot;"; f1s1=substr(f1s1,f1n1+1)}
  f1s1=f1s2 f1s1
  f1s2=""                                         #  "%" -> "&#x25;"
  while(f1n1=index(f1s1,"%")){f1s2=f1s2 substr(f1s1,1,f1n1-1)"&#x25;"; f1s1=substr(f1s1,f1n1+1)}
  f1s1=f1s2 f1s1
  if(length(f1s1)>999){                           #  обрезка слишком длинных литералов
    f1n1=index(substr(f1s1,1000),"•")
    if(!f1n1)f1n1=1000
    else   f1n1=f1n1+999                          #  v-вместо обрезанного - справка о факте ;)
    f1s1=substr(f1s1,1,f1n1-1)"•(+"(length(f1s1)-f1n1)" chars)"
  }
  return ("\""f1s1"\"")                           #  результат "закавычивается!"
}

END{                                              #  отстрелялись; ну почти...
  if(errS!=""){
    print("")
    print("] "errS)                               #  есть проблем-с => подудим
    exit 1                                        #  и на выход с вещами
  }
  mkLn()
  if(fNum%100)print("")
  else print(" = "fNum)

  jDat=strftime("%Y.%m.%d")
  if(jLst=="")jLst=wDir maxD".hidn.addS"
  split("",aSel)
  nSel=0
  for(j0=1;j0<=nMig;j0++){                        #  вычленим записи со свежей datJ
    split(aMig[j0],aj)
    if(aj[2]==maxD)aSel[++nSel]=aMig[j0]
  }
  asort(aSel)
  j1=fNum":allS "nSel":good "maxD":dEnd "jDat":done"
  print("; stat "j1)                                                                  > jLst
  print(";     datS_timS.1    datJ.2    hitP.3 id_P.4 \"namP\".5 \"moto\".6 id_F.7")  > jLst
  for(j0=1;j0<=nSel;j0++)print(aSel[j0])                                              > jLst
  print(j1,systime()-tIni":sec")
}

    Имя файла скрипта: "1hidn.cmd" V-текст скрипта-V
- - - - - - - - -
@if not defined nJob @echo off
:: 0addS.cmd - сборка дополнительного списка гггг.мм.дд.hidn.addS разделов-невидипок по спискам форм
:: 0. нужны списки: _lost.addF /что качать/ и *.~.mig.bStat (можно строить наше новое)
:: 1. проверяется наличность и окружение
:: 2. скачиваются статистики скрытников
:: 3. строится список "datJ.stat,aadS" с пригодными статистиками 200 или более хитов за "12мес"
::  основной и резервный URL Самиздата (желательно работать с резервом)
echo %date% %time:~0,8% %~nx0 Begs
set siUrl=samlib.ru
if exist \siUrl.budclub set siUrl=budclub.ru
:: 2023.06.28, nvv, сознано на основе aHits.cmd от 2023.05.30
:: 2023.08.12, nvv, last upDates

:: основные сеты и прочая подготовка к работе...
set nJob=addS
set jDat=%date%
set errInf=
::  wrks - общее время работы процесса (пуск общего таймера)
for /F %%A in ('gawk.exe "BEGIN{print(systime());exit}"') do set wrks=%%A
::  aScr - скрипт вычленения годных потеряшек по страницам их статистик
set aScr=%~dpn0.awk
if NOT exist %aScr% echo NO %aScr% found & exit
::  wDir - общее рабочее место
set wDir=%~d0\Jobs
::  jDir - место для оперативных файлов
set jDir=%wDir%\_%nJob%
::  zDir - общий подкаталог для посуточных подкаталогов с итоговыми списками
set zDir=%~d0\Stor\wOld\%jDat%
::  jRpt - общий отчёт с итоговыми сводками процессов
set jRpt=%wDir%\_siTop.#
::  dnLd - счётчик скачанного ([пере]запустим "таймер" ratW)
set dnLd=0
::  wGetSiz - размер скачанного файла (default 1b минимум)
set wGetSiz=1
:: проверить/создать нужные структуры для работы (оперативные площадки)
if NOT exist %jDir%\*.* (
  mkDir %jDir%
  if errorLevel 1 set errInf=fail mkDir %jDir% & exit
)
if NOT exist %zDir%\*.* (
  mkDir %zDir%
  if errorLevel 1 set errInf=fail mkDir %zDir% & exit
)
:: для работы нужно наличие *.~.lst.bStat /ПОЧТИ готовый расчёт/ из имени берём datJ
set datJ=
for %%A in (%wDir%\20??.??.??.~.lst.bStat) do set datJ=%%~nA
if NOT defined datJ echo NO *.~.lst.bStat found & exit
set datJ=%datJ:~0,10%
:: для работы нужен список потеряшек _lost.addF
set inFl=%wDir%\_lost.addF
if NOT exist %inFl% echo NO %inFl% found & exit
::  jLst - конечный результат процесса ДОЛЖНЫЙ обновиться /или появиться/
set jLst=%wDir%\%datJ%.hidn.%nJob%
:: может сегодня уже ВСЁ сделано?
if exist %jLst% echo fresh %jLst% found, exit & exit
:: если СТАРОГО списка нет, значит это ПЕРЕЗАПУСК после падения, ПРОДОЛЖИМ скачивание
if NOT exist %wDir%\*.hidn.%nJob% goto chkDl
:: ок, работать можно, а нужно? Спросим...
rem set tmp0=
rem echo old *.stat.%nJob% found; upDate it? [yes] == y
rem set /p tmp0=?[no]?
rem if /i NOT %tmp0%. == y. (
rem   echo job canceled {NO to upDate}
rem   exit
rem )
:: сегодня ничего пока не сделано... а старое есть/?/ == стираем
if exist %jDir%\*.stat.htm (
  erase /F /Q %jDir%\*.stat.htm
  if errorLevel 1 set errInf=fail erase %jDir%\*.stat.htm & goto nExit
)
:: и устаревший итоговый список туда же /нафик/
erase /F /Q %wDir%\*.hidn.%nJob%
if errorLevel 1 set errInf=fail erase *.hidn.%nJob% & goto nExit

rem ; 18:form 7689:pags 1536388:txts 104325:auth 1927:hidn 2023.07.03:done

:chkDl выясним имеющийся расклад по потеряшкам после обработки /процессом rAddF.cmd/ форм
for /F "tokens=1-9,*" %%A in (
  ' gawk.exe "{if(match($2,/^18:form$/))print($3,$4,$5,$6);exit}" %inFl% '
) do (
  set pags=%%A
  set txts=%%B
  set auth=%%C
  set hidn=%%D
)
:: почистим, вычленив цифры
set pags=%pags:~0,-5%
set txts=%txts:~0,-5%
set auth=%auth:~0,-5%
set hidn=%hidn:~0,-5%
echo = %pags%.pags %txts%.txts %auth%.auth %hidn%.hidn.stats dnLd...

:: ок, скачаем статистики ВСЕХ этих /скрытых/ разделов
set cNum=0
set dnLd=0
::  ratW - темп скачивания
for /F %%J in ('gawk.exe "BEGIN{print(systime());exit}"') do set ratW=%%J
:: скачивание недостающего
for /F "tokens=*" %%A in (%inFl%) do call :dLit %%A
:: вычислим темп скачивания ratW
if %dnLd% == 0 (
  set ratW=0
) else (
  for /F %%A in (
    ' gawk.exe "BEGIN{print(sprintf(\"%%3.2f\",(systime()-%ratW%)/%dnLd%));exit}" ' 
  ) do set ratW=%%A
)
:: ок, статистики у нас
echo = %dnLd%:dnLd.stats %ratW%:ratW looking for good id_P
:: вычленим годные/нужные для расчёта /в список %datJ%.stat.add2h/
gawk.exe -f %aScr% %jDir%\*.stat.htm
if NOT %errorLevel% == 0 set errInf=%aScr% problem in %jDir%\*.stat.htm & goto nExit
rem ; stat 1940:allS 20:good 2023.06.28:dEnd 41:tNum 2023.07.03:done
rem ; stat 1939:allS 1112:good 2023.07.19:dEnd 2023.07.23:done
for /F "tokens=1-9,*" %%A in (
  ' gawk.exe "{if(match($2,/^stat$/))print($3,$4,$5);exit}" %jLst% '
) do (
  set allS=%%A
  set good=%%B
  set dEnd=%%C
)
:: почистим, вычленив цифры
set allS=%allS:~0,-5%
set good=%good:~0,-5%
set dEnd=%dEnd:~0,-5%
echo = %allS%.allS %good%.good %dEnd%.dEnd

:: остановим общий "таймер", : преобразуем секунды в время работы (в обычном формате "чч:мм:сс")
for /F %%A in ('gawk.exe "BEGIN{print(strftime(\"%%H:%%M:%%S\",systime()-%wrks%+46800));exit}"') do set wrks=%%A

set tmp0=;0S add2 ^
%pags%.pags ^
%txts%.txts ^
%auth%.auth ^
%hidn%.hidn ^
%good%.good ^
%dnLd%:dnLd ^
%ratW%~ratW ^
%jDat%:done ^
%wrks%~wrks ^
%time:~0,5%
echo %tmp0% >> %jLst%
echo %tmp0% >> %jRpt%
echo = Ok %hidn%:hidn %good%:good %dnLd%:dnLd %ratW%~ratW
:: скинем результаты в хранилище
copy /y /b %jLst% %zDir%\ > nul
if errorLevel 1 set errInf=fail copy %jLst% TO %zDir% & goto nExit
:: подудим где нада "готово-с!"
set itog=%jLst%

:nExit
if defined errInf if NOT defined errF echo ! %errInf%
echo %date% %time:~0,8% %~nx0 Ends
goto :eof

rem ;txTot id_P
rem    25 k/koroljuk_m_a

:dLit %%A скачивание статистик по id_P {like e/etno} из списка lost.add2h
set id_P=%2
set wFil=%id_P%
set wFil=%jDir%\%wFil:~0,1%~%wFil:~2%.stat.htm
if exist %wFil% (
  call :show ~
  goto :eof
)
call :show .
set wUrl=budclub.ru/%id_P%/stat.shtml
call wGetUrl2fil.cmd %wUrl% %wFil%
:: проблема?
if %wGetErr% == 1 set errInf=no %wUrl%
if %wGetErr% == 3 set errInf=no web{SI}
goto :eof

:show оживляж скачивания статистик
set /a cNum+=1
set mark=%1
rem if exist %wFil% set mark=~
if %cNum:~-1% == 0 set mark=%cNum:~-2,1%
gawk.exe "BEGIN{printf(\"%mark%\");exit}"
rem if NOT "%cNum:~-2%" == "00" if NOT "%cNum:~-2%" == "50" goto :eof
if NOT "%cNum:~-2%" == "00" goto :eof
:: по 50 отметок на строку
set tmp0=    %cNum%
set tmp0=%tmp0:~-5,3%
gawk.exe "BEGIN{printf(strftime(\"%tmp0% %%H:%%M:%%S\n\",systime()-%ratW%,-1));exit}"
goto :eof

    Имя файла скрипта: "aHits.awk" V-текст скрипта-V
- - - - - - - - -
BEGIN{ # aHits.awk - просмотр локальных страниц рейтинга и создание списка разделов с хитами

# 2023.07.23, nvv, переделано для включения потеряшек add2 в aHits
#  ! прочая история в подвале

# В процессе: считывается дата обновления рейтинга, и количество страница рейтинга хитов
# Источник - рейтинг "по хитам": /rating/hits/ может использоваться самостоятельно.
# Для правильной нумерации мест разделов, ИМЯ входных файлов должно начинаться с ЧИСЛА
# (номер страницы рейтинга,например: 0025.html). Расширение - любое.
# Причём, итоговые списки (пере)создаются РЯДОМ (jDir) с входными файлами:
# - fOut=jDir"]aHits.lst" # основной список - OK_элементы ]aHits.lst
# - fBad=jDir"]aHits.bad" # список брака - BAD_элементы ]aHits.bad
# + выдача на StdUot: hDat,pMax,xNam,xNum,xPas,hMax,h30k,hTot
# hitP - par.hits - хиты раздела за "год"
# numP - par.number - место раздела в рейтинге СИ по посещаемости (1..10000) первые 10k
# sizK - общий объём (в kb) текстов раздела
# sizN - общее количество текстов в разделе
# id_P - partition (par.) - раздел СИ (like: n/nosow_w_w)
# namP - "par.name" - название раздела (псевдоним автора, like: "Влад" или "Уксус•Сергей")

# НЕштат (НЕ рейтинг или НЕ в порядке)  выдача на StdUot:
# "] Hole or no data ("step" lines) in file:" fNam)
# "] DifDates;" hDat "!=" cYea"."cMth"."cDay "! in file:" fNam)
# "] DifPageNums;" pMax "!=" pNum "! in file:" fNam)
# с немедленным завершением работы

# в fOut: исправные записи с хитами разделов за "12мес":
#   hitP  numP    sizK   sizN id_P        "namP" "sigP"
#  24620   192   14754     25 n/nosow_w_w "Влад" "nvv_Lazy_Tiger"
# 901899     1     931      5 d/dront_n "Дронт•Николай" "Записки•о•том,•чего•не•было"
#  18625   385   18053     29 n/nosow_w_w "Влад" "nvv_Lazy_Tiger"

# в fBad: записи с проблемами:
# ] Err:2 no nick:    829  8443 u/ulybin_w_w ""
# ] Err:1 no text:  44158   147 s/sherbakow_a "Щербаков•Александр"

  if(ARGV[1]=="")exit                             # защита от случайного пуска
  j1=split(ARGV[1],aj,"\\")
  for(str2 in ENVIRON){                     #  ! регистр букв в имени переменной может быть любой
    if(tolower(str2)=="wdir")wDir=ENVIRON[str2]"\\" #  место для итогового списка aHits
    if(tolower(str2)=="add2")add2=ENVIRON[str2]   #  место для итогового списка aHits
  }
  if(add2=="")add2="_hidn.addS"         #  если вызов НЕ из CMD
# ;     datS_timS.1    datJ.2    hitP.3 id_P.4 "namP".5 "moto".6 id_F.7
# 2023.07.20_01:54:05 2023.07.19   9073 n/neon13 "Neon13" "Авторский" n~neon13
  nAdd=0
  while((getline line<add2)>0){                   #  список потеряшек
    split(line,aj)
    if(match(aj[1],/^20/)){                       #  строка с данными скрытого раздела
      j0=1000000-aj[3]
      aAdd[++nAdd]=sprintf("%7s%7s%8s%7s",j0,0,0,1)" "aj[4]" "aj[5] # это на включение в итог
      aIdA[aj[5]]=nAdd                          #  это для контроля отсутствия включаемого в итоге
    }
  }
  close(add2)                                     #  всё/не_всё, - старый список тогось
  add2=0
#  500779      1   46664    126 g/goncharowa_g_d "Гончарова•Галина•Дмитриевна" "Писатель•должен•быть•талантлив•и•дьявольски•трудолюбив"
#       3 105072       0      0 d/dura "Не•Писатель" 
# ; hitP.1 numP.2 sizK.3 sizN.4 id_P.5 "namP".6 "moto".6 
    
#   if(j1==1)jDir=".\\"                           # иесто для результатов ТАМ ЖЕ, где исходные файлы
#   else for(j2=1;j1-j2>0;j2++)jDir=jDir aj[j2]"\\"
#   fBad=jDir"]aHits.bad"                           # список брака - BAD_элементы ]aHits.bad
  jTic=systime()                                  # ДВЕ проверки: дата(step==-1) + полнота(==0)
  jYea=strftime("%Y",jTic)
  jMth=strftime("%m",jTic)
  xNam=0                                          # счёчик разделов (записей) без ФИО
  xNum=0                    # счёчик разделов без наполнения (без объёмя и кол-ва файлов раздела)
  xPas=0                                          # счёчик разделов "всего OK"
  hTot=0                                          # счёчик "hits total"
  hMax=0                                          # хиты 10k разделов "макс"
  h30k=99999                                      # хиты 10k разделов "мин"
  pMax=""                                         # номер страницы рейтинга (в имени файлов)
}

{                                                 # // -=- основной блок скрипта
  if(FNR==1){           # секция завершения работ с одним файлом и подготовки к работе со следущим
    if(NR!=1)chkSteps()                           # страница пройдена; она в порядке?
    fNam=FILENAME                                 # U:\Jobs\#aHits\0001.hits.htm
    p100=split(FILENAME,aj,"\\")  # NEW/NEXT входной файл разделим на подкаталоги и имя.расширение
    p100=split(aj[p100],aj,".")                 # имя.расширение входного файла разделим на части
    p100=(aj[1]-1)*100      # p100 чистые сотни (начиная с нуля, like: "1400" для "0015.hits.htm")
    step=-1                                       #  -1 == date, 0 - nPgs, 1..100 dataLine
    fNum++
    if(fNum%100==0) print(sprintf(": %4s",fNum/100))
    else if(fNum%10)printf(".")
    else            printf(":")                   #  оживляж...
  }
# <b>СТАТИСТИКА за 22/11:</b>
  if (step==-1) {                                 # считывание обязательной ДАТЫ рейтига
    if(match($0,/^<b>СТАТИСТИКА за ([0-3][0-9])\/([01][0-9]):<\/b>$/,aj)){  # 30 09 (30/09)
      cMth=aj[2]
      cDay=aj[1]
      step++
    }
  }
# <center><b>Страниц (970):</b>  <b>1</b> <a href=
  if (step==0) {                                  # проверка ПОЛНОТЫ статистики
    if(match($0,/^<center><b>Страниц \(([0-9]+)\):<\/b> /,a2)){
      pNum=a2[1]
      step++
    }
  }
# <DL><b>44158</b> <a href=/s/sherbakow_a/>Щербаков Александр</a> "Творчество как бы"<DD><font color=#555555><i>Почитал ГкН, Лунного скульптора, Игру... и зачесались ручки как бы... что получится - будет видно)</i></font></DD></DL>
# <DL><b>43514</b> <a href=/a/aizmash/>Aizmash</a> "aizmash" (1603k,5)</DL>
# <DL><b>721</b> <a href=/g/glinka_i_s/></a> "Ярина" (924k,16)</DL>
# <DL><b>593</b> <a href=/a/amelichewa_a_a/>Амеличева Алиса Анатольевна</a> "" (4k,23)</DL>

  if (step+0>0) {                                 # извлечение хитов, authID и ФИО(псевдонима)
    numP=p100+step
    if (match($0,/<b>([0-9]+)<\/b> <a href=\/(.)\/(.+)\/>(.*)<\/a>/,aj)){ # это обязательно!
      hitP=aj[1]                                  # хиты раздела за "12мес"
      id_P=aj[2]"/"aj[3]                          # ID раздела
      if(aj[4]==""){                              # no: ФИО/псевдоним/nick
        xNam++                                    # errInf hitP numP id_P
        namP="\">?<\""                            # ВМЕСТО ФИО/ник_владельца - спец.отметка
        sizK=0                                    # а объём - обнуляем
        sizN=0                                    # как и кол-во
        moto=""                                   # как и девиз
      }else{
        namP=pack(aj[4])                          # ФИО/ник_владельца раздела
        if(match($0,/<\/a> "(.*)" \(([0-9]+)k,([0-9]+)\)/,a2)){ # (3776k,10) это - если не пусто
          xPas++
          moto=pack(a2[1])                        # девиз раздела (может отсутствовать)
          sizK=a2[2]                              # общий объём текстов в разделе
          sizN=a2[3]                              # кол-во текстов в разделе
        } else {                                  # пусто (нет наполнения раздела)
          xNum++                                  # список брака - BAD_элементы ]aHits.bad
          moto=""
          sizK=0
          sizN=0
        }
      }
      aHit[++nHit]=sprintf("%7s%7s%8s%7s",1000000-hitP,numP,sizK,sizN)" "id_P" "namP" "moto
      aIdH[id_P]=numP
      hTot=hTot+hitP                              # счёчик "hits total"
      if(numP==30000)h30k=hitP                    # хиты 30-и тысячного раздела
      if(hMax-hitP<0)hMax=hitP                    # хиты - "макс"
      step++
    }
  }
}

function pack(j1) {             # заменить мешающие символы на нейтральные + закавычить результат
  j2=""                                           # TAB -> "•"
  while(n=index(j1,"\x09")) { j2 = j2 substr( j1, 1, n-1 ) "•"; j1 = substr( j1, n+1 ) }
  j1=j2 j1
  j2=""                                           # пробел -> "•"
  while(n=index(j1," ")) { j2 = j2 substr( j1, 1, n-1 ) "•"; j1 = substr( j1, n+1 ) }
  j1=j2 j1
  j2=""                                           # неразрывный_пробел -> "•"
  while(n=index(j1,"\xA0")) { j2 = j2 substr( j1, 1, n-1 ) "•"; j1 = substr( j1, n+1 ) }
  j1=j2 j1
  j2=""                                           # двойные кавычки -> "”"
  while(n=index(j1,"\"")) { j2 = j2 substr( j1, 1, n-1 ) "&quot;"; j1 = substr( j1, n+1 ) }
  j1=j2 j1
  j2=""                                           # "••" -> "•"
  while(n=index(j1,"••")) { j2 = j2 substr( j1, 1, n-1 ); j1 = substr( j1, n+1 ) }
  j1=j2 j1
  if(substr(j1,1,1)=="•") j1 = substr( j1, 2 )    # del "•" at beg
  n=length(j1)
  if(substr(j1,n,1)=="•") j1 = substr( j1, 1, n-1 ) # del "•" at end
  return ("\""j1"\"")
}

function chkSteps() {                             # страница пройдена; она в порядке?
  if((step!=101)&&(pNum*100!=p100+100)){ #количество записей на странице штатное? если НЕ последняя
    print pNum*100,p100
    print("] Hole or no data ("step-1" lines) in file:" fNam) # нет! (мало записей)
    exit 3
  }else{
    if(hDat==""){                                 # это ПЕРВЫЙ файл страницы с хитами
      cYea=jYea                         # отпределимся с датой статистики (относительно "сегодня")
      if(jMth!=cMth) if(cMht==12)cYea--   # если месяц иной + предыдущий - декабрь => прошлый год
      hDat=cYea"."cMth"."cDay                     # полная дата для: "СТАТИСТИКА за дд/мм"
    }else if(hDat!=cYea"."cMth"."cDay){         # это второй и последующие файлы, даты совпабают?
      print("] DifDates: " hDat "!=" cYea"."cMth"."cDay " in file:" fNam) # нет!
      exit 1
    }
    if(pMax=="")pMax=pNum     # это ПЕРВЫЙ файл с хитами, запомним общее кол-во страниц в рейтинге
    else if(pMax!=pNum){      # это вторай и последующие файлы "с хитами", это кол-во тоже самое?
      print("] DifPageNums: " pMax "!=" pNum " in file:" fNam)  # нет!
      exit 2
    }
  }
}
# 2018.10.25, nvv, created
# ...
# 2019.01.02, nvv, update
# 2019.02.05, nvv, ФИО/nick упаковывается + новое поле size,num раздела (like "10496k,32")
# 2019.04.19, nvv, added hTot - summ for all hits of 100 partitions, paged
# 2019.07.10, nvv, мелкая косметика
# 2019.07.27, nvv, добавлено поле "девиз раздела"
# +наполнение раздела - теперь это два поля(объём/кол-во) +поля слегка переставлены
# 2020.01.27, nvv, мелкая косметика
# 2020.03.13, nvv, переделано и обустроено всё потребное (слегка-с новая концепция)
# 2020.08.21, nvv, исправлена мелкая, но неприятная ошибка отбраковки разделов с пустым девизом
# 2020.09.10, nvv, нештат НЕ исключается из итогового списка, НО кол-во у них всегда(!) 0
#   + соответственно, список "плохого" (fBad) НЕ создаётся
# 2020.11.01, nvv, последняя страница рейтинга не обязана иметь 100 записей. ;)
#   + на прочих - показывает точное число (без +1)
# 2021.05.02, nvv, переименовано (убран префикс "siTip-")

END{
  if(ARGV[1]==""){                                #  защита от случайного пуска
    print("NO in file(s)")
    exit -1
  }
  if(fNum%100)print("")
  else print(" = "fNum)
  chkSteps()                                      # страница пройдена; она в порядке?
  for(id_P in aIdA)if(!(id_P in aIdH)){
    aHit[++nHit]=aAdd[aIdA[id_P]]
    add2++
  }
  fOut=wDir hDat".aHits"                           # основной список - OK_элементы ]aHits.lst
  jDat=strftime("%Y.%m.%d")
# ;1A hits 5:xNam 451:xNum 104642:xPas 2023.07.09:hDat 507217:hMax 1051:hPag 15:add2 1086:dnLd 0.17~ratW 2023.07.11:done 03:12:17~wrks 15:02 
# ; aHit 5:xNum 451:xNum 104642:xPas 507217:hMax 1051:hPag 15:add2 381:h30k 42411880:hTot 2023.07.09:hDat 2023.07.14:done 
  print("; aHit "xNam":xNam",xNum":xNum",xPas":xPas",hMax":hMax",pMax":hPag",add2":add2"\
,h30k":h30k",hTot":hTot",hDat":hDat",jDat":done ") >fOut # общая сводка работы с рейтингом "по хитам"
  print("; hitP.1 numP.2 sizK.3 sizN.4 id_P.5 \"namP\".6 \"moto\".6")     > fOut
  asort(aHit)
  for(j0=1;j0<=nHit;j0++){
    print(sprintf("%7s",1000000-substr(aHit[j0],1,7)),substr(aHit[j0],9)) > fOut
  }                                               #  общая сводка работы с рейтингом "по хитам"
  print("; hitP.1 numP.2 sizK.3 sizN.4 id_P.5 \"namP\".6 \"moto\".6")     > fOut
  print("; aHit "hDat,pMax":pMax",xPas":xPas "add2":add2 "h30k":h30k")
}

    Имя файла скрипта: "aHits.cmd" V-текст скрипта-V
- - - - - - - - -
@if not defined nJob @echo off
:: aHits.cmd - сборка полного списка авторов СИ по рейтнгу хитов
::  основной и резервный URL Самиздата (желательно работать с резервом)
echo %date% %time:~0,8% %~nx0 Begs
set siUrl=samlib.ru
if exist \siUrl.budclub set siUrl=budclub.ru
:: %time% - HH:MM:SS,tt HH<=9 -> "HH" == " H" лидирующий пробел для часа меньше 10
:: %date% - YYYY.mm.dd

:: 2023.07.30, nvv, исправлена мелкая неточность выхода по несоблюдению условий для работы
:: история изменений в подвале скрипта!

:: основные сеты и прочая подготовка к работе...
set nJob=aHits
set jDat=%date%
set errInf=
::  wrks - общее время работы процесса (пуск общего таймера)
for /F %%A in ('gawk.exe "BEGIN{print(systime());exit}"') do set wrks=%%A
::  aScr - скрипт обработки страниц рейтинга "по хитам"
set aScr=%~dpn0.awk
if NOT exist %aScr% echo ! NO %aScr% found & exit
::  wDir - общее рабочее место
set wDir=%~d0\Jobs
::  jDir - место для оперативных файлов
set jDir=%wDir%\#%nJob%
::  jRpt - общий отчёт с итоговыми сводками процессов
set jRpt=%wDir%\_siTop.#
::  zDir - общий подкаталог для посуточных подкаталогов с итоговыми списками
set zDir=%~d0\Stor\wOld\%jDat%
::  dEnd - верхняя дата интервала расчёта, обновлять aHits резонно, только по гоговности огого начерно "~"
set dEnd=
for %%A in ( %wDir%\20??.??.??.~.lst.bStat ) do set dEnd=%%~nA
if NOT defined dEnd echo NO *.~.lst.bStat found, NO aHits upDate & exit
set dEnd=%dEnd:~0,10%
::  add2 - список потеряшек к расчёту для включений в aHits
set add2=%wDir%\%dEnd%.hidn.addS
if NOT exist %add2% echo x NO updted *.hidn.addS, NO aHits upDate & exit
echo add2.Ok
::  oNmD - дата СИ последненго /актувльного/ aHits /имя файла/
::  oMax - хиты 1 номера - '' - '' - '' - '' - '' - /максимальные в нём/
::  oPgs - число страниц - '' - '' - '' - '' - '' - /всего/
::  oDrD - дата сборки   - '' - '' - '' - '' - '' -  /подкаталог в zDir, ГДЕ искать/
set oDrD=
::  0. выясняем ситуацию aHits касаемо в логе общей истории jRpt _siTop.#
for /F "tokens=1-9,*" %%A in (
  ' gawk.exe "{if(match($0,/^;1A hits.* ([^ ]+):hDat,* ([^ ]+):hMax ([^ ]+):hPag .* ([^ ]+):done/,a))print a[1],a[2],a[3],a[4]}" %wDir%\_siTop.#  '
) do (
  set oNmD=%%A
  set oMax=%%B
  set oPgs=%%C
  set oDrD=%%D
)
::  last - последний актуальный список aHits
set last=%~d0\Stor\wOld\%oDrD%\%oNmD%.%nJob%
if NOT exist %last% set errInf=no last %last% found & goto nExit
::  oPag - минимальное годное число страниц: pMax в СИ == oPgs-1 /текущее локальное значение/
set /a oPag=%oPgs%-1
set tmp0=%oNmD:~8%/%oNmD:~5,2%
echo # loc: %tmp0% oMax:%oMax% pOld:%oPgs%
:: - "сегодня" == всё уже сделано? == выход
if .%oDrD% == .%jDat% echo toDay's %last% found, exit & exit
::  fil1 - первый файл комплекта страниц aHits
set fil1=%jDir%\0001.hits.htm
::  oPag минимум страниц в рейтинге, должно быть не меньше
set oPag=1050
::  dnLd - счётчик скачанного ([пере]запустим "таймер" ratW)
set dnLd=0
::  wGetErr - уточнённый статус завершения wGet
set wGetErr=0
::  wait - ФЛАГ/таймер зацикливания ожидания полного списка aHits
set wait=%1
:: если заказано зацикливание, цикл ожиданий - 5 минут (300 секунд)
if defined wait set /a wait=60*10
:: проверить/создать нужные структуры для работы (оперативные площадки)
if NOT exist %jDir%\*.* (
  mkDir %jDir%
  if errorLevel 1 set errInf=fail mkDir %jDir% & goto nExit
)
if NOT exist %zDir%\*.* (
  mkDir %zDir%
  if errorLevel 1 set errInf=fail mkDir %zDir% & goto nExit
)
:: 1. смотрим наличие aHits в Jobs - нет == переходим к п.4
if NOT exist %wDir%\%oNmD%.%nJob% goto doJob
:: 2. aHits есть, ок, работать можно, а нужно? Спросим...
:: - отказ == выход
set tmp0=
echo old *.%nJob% found; upDate it? [yes] == y
set /p tmp0=?[no]?
if /i NOT %tmp0%. == y. (
  echo job canceled {NO to upDate}
  exit
)
:: 4. нужно! ...а старые *,htm есть/?/ == стираем
if exist %jDir%\*.hits.htm (
  erase /F /Q %jDir%\*.hits.htm
  if errorLevel 1 set errInf=fail erase %jDir%\*.hits.htm & goto nExit
)
:: и устаревший итоговый список *.aHits туда же /нафик/
if exist %wDir%\%oNmD%.%nJob% (
  move /Y  %wDir%\%oNmD%.%nJob% %wDir%\%oNmD%.%nJob%.old
  if errorLevel 1 set errInf=fail move %oNmD%.%nJob% 2old & goto nExit
)

:doJob выяснение ситуации с "потеряшками" Добавляем их?
:: - скачиваем fil1 и переходим на анализ п.1....
:: 4. смотрим fil1
:: -  нет fil1 или он НЕ сегодняшний == СКАЧИВАЕМ сегодняшний dnLd++
:: 5.  анализ сегодняшнего статуса
:: - fil1 - "НЕ годный" 
:: -- dnLd>=1 выход или цикл ожидания
:: -- dnLd==0 скачиваем свежую копию + переход на р5.
:: - fil1 - "ГОДНЫЙ" докачиваем и строим
:Again  curM - кол-во секунд с момента начала цикла ожидания
if NOT exist %fil1% (
  call :reqSI 1
  if defined errInf goto nExit
)
if NOT exist %fil1% set errInf=fail dnLd 0001.hits.htm & goto nExit
call :scan01
if defined errInf goto nExit
:: если изменились максимальные хиты топа + страниц не меньше, чем старое-1 == скачиваем!
if NOT %oMax% == %hMax% if %pMax% GEQ %oPag% goto dlAll
:: в СИ или то же самое, или мало страниц, скачивать нельзя.
erase %fil1%
if errorLevel 1 set errInf=fail erase %fil1% & goto nExit
:: неполный список; цикл заказывали?
if NOT defined wait (
  echo x web: %cDat% hMax:%hMax% pMax:%pMax% too LOW, no wait, exit
  goto nExit
)
:: а заказано именно ЧИСЛО (секунд в цикле ожидания)?
for /F %%A in (
  'gawk "BEGIN{print(strftime(\"%%H:%%M:%%S\",systime()+%wait%));exit}"'
) do set tmp0=%%A
:: не число - не судьба... ;)
if %tmp0% == 0 goto nExit
:: иначе ждём-с ;)
echo ! web: %cDat% hMax:%hMax% pMax:%pMax% too LOW {was:%oPgs%}, WAITing till %tmp0%
call :waitSecs %wait%
rem gawk "BEGIN{t=systime()+%wait%;while(t>systime()){printf(\"%%s\",\".\");j=systime()+1;while(j>systime()){k=1}};exit}"
rem echo.
goto Again

:dlAll скачивание всего комплекта страниц aHits
echo v web: %cDat% hMax:%hMax% pMax:%pMax% OK, dnLd...

::  ratW - темп скачивания (секунд на один файл в среднем)
for /F %%A in ('gawk.exe "BEGIN{print(systime());exit}"') do set ratW=%%A
gawk.exe "BEGIN{printf(\"%pMax%.pMax %hMax%.hMax\n.\");exit}"
for /L %%A in (2,1,%pMax%) do if NOT defined errInf call :reqSI %%A
if defined errInf goto :eof
if NOT "%pNum:~-2%" == "00" echo. = %pNum% %time:~0,8%
:: вычислим темп скачивания ratW
if %dnLd% == 0 (set ratW=0) else (
for /F %%A in ('gawk.exe "BEGIN{print(sprintf(\"%%3.2f\",(systime()-%ratW%)/%dnLd%-1));exit}"') do set ratW=%%A
)

echo = dnLd ok, scaning...
gawk.exe -f %aScr% %jDir%\*.hits.htm
if NOT %errorLevel% == 0 set errInf=fail %aScr% with *.hits.htm & goto nExit
set jLst=
:: ищем готовый ПОЛНЫЙ 20??.??.??.aHits, берём дату в его имени
for %%A in (%wDir%\20??.??.??.%nJob%) do (
  set hDat=%%~nA
  set jLst=%%A
)
if NOT defined jLst set errInf=fail make %jLst% & goto nExit
rem ; aHit 5:xNam 451:xNum 104642:xPas 507217:hMax 1051:hPag 15:add2 381:h30k 42411880:hTot 2023.07.09:hDat 2023.07.14:done 
rem  1 ;
rem  2 aHit
rem  3          5 xNam 1
rem  4        451 xNum 2
rem  5     104642 xPas 3
rem  6     507217 hMax 4
rem  7       1051 hPag 5
rem  8         15 add2 6
rem  9        381 h30k 7
rem 10   42411880 hTot 8
rem 11 2023.07.09 hDat 9
rem 12 2023.07.14 done
:: считаем итоги сборки списка aHits
for /F "tokens=1-9,*" %%A in (
 ' gawk.exe "{if(match($2,/^aHit$/)){print $3,$4,$5,$6,$7,$8,$9,$10;exit}}" %jLst% '
) do (
  set xNam=%%A
  set xNum=%%B
  set xPas=%%C
  set hMax=%%D
  set hPag=%%E
  set add_=%%F
  set h30k=%%G
  set hTot=%%H
)
:: вычленим значения данных
set xNam=%xNam:~0,-5%
set xNum=%xNum:~0,-5%
set xPas=%xPas:~0,-5%
set hMax=%hMax:~0,-5%
set hPag=%hPag:~0,-5%
set add_=%add_:~0,-5%
set h30k=%h30k:~0,-5%
set hTot=%hTot:~0,-5%
:: остановим "таймер", : преобразуем секунды в время работы (в обычном формате "чч:мм:сс")
for /F %%A in ('gawk.exe "BEGIN{print(strftime(\"%%H:%%M:%%S\",systime()-%wrks%+46800));exit}"') do set wrks=%%A
:: оформим подвал списка...
:: ;1A hits 5:xNam 451:xNum 104642:xPas 2023.07.09:hDat 507217:hMax 1051:hPag 15:add_ 1086:dnLd 0.17~ratW 2023.07.11:done 03:12:17~wrks 15:02 
set tmp0=;1A hits ^
%xNam%:xNam ^
%xNum%:xNum ^
%xPas%:xPas ^
%hDat%:hDat ^
%hMax%:hMax ^
%hPag%:hPag ^
%add_%:add2 ^
%h30k%:h30k ^
%dnLd%:dnLd ^
%ratW%~ratW ^
%jDat%:done ^
%wrks%~wrks ^
%time:~0,5%
echo %tmp0% >> %jLst%
echo %tmp0% >> %jRpt%
echo = Ok %xNam%:xNam %xNum%:xNum %xPas%:xPas %hMax%:hMax %h30k%:h30k %hTot%:hTot %add_%:add2 %hDat%
:: прикопаем итоги для истории...
copy /b %jLst% %zDir% > nul
if errorLevel 1 set errInf=fail copy %jLst% TO %zDir% & goto nExit
copy /b %fil1% U:\Jobs\#aHits\hist.aHits\0001.hits.%jDat%.htm  > nul
if errorLevel 1 set errInf=fail copy %fil1% TO hist.aHits & goto nExit
for %%A in ( %fil1% ) do echo %%~tA >> U:\Jobs\#aHits\hist.aHits\daTim_of_0001.hits.~.htm.#
:: и устаревший итоговый список *.aHits туда же /нафик/
if exist %wDir%\%oNmD%.%nJob%.old (
  erase /F /Q %wDir%\%oNmD%.%nJob%.old
  if errorLevel 1 set errInf=fail erase %oNmD%.%nJob%.old & goto nExit
)
:: подудим где нада "готово-с!"
set itog=%jLst%

:nExit
if defined errInf (
  echo ! %errInf%
  if NOT defined wait set errInf=
)
echo %date% %time:~0,8% %~nx0 Ends
goto :eof

:reqSI %%J притаскивает file:///U:/Jobs/#aHits/index-3.shtml
set pNum=000%1
set tmp0=   %1
::  pNum - 4-х значное число с лидирующими нулями (для красивой сортировки пронумерованных страниц рейтинга)
set pNum=%pNum:~-4%
set tmp0=%tmp0:~0,2%
::  wUrl - первая /дефолтная/ страница рейтинга
set wUrl=%siUrl%/rating/hits/index-%1.shtml
::  wFil - конечный локальный файл для скачиваемого
set wFil=%jDir%\%pNum%.hits.htm
:: оживляж...
set mark=.
if exist %wFil% set mark=~
if %pNum:~-1% == 0 set mark=%pNum:~-2,1%
:: первая странимца нужна для выяснения числа страниц
if %1 == 1 (
  set wUrl=%siUrl%/rating/hits/index.shtml
  goto doDl
)
gawk.exe "BEGIN{printf(\"%mark%\");exit}"
if NOT "%pNum:~-2%" == "00" goto doDl
echo. = %pNum:~0,2% %time:~0,8%
:doDl
if exist %wFil% goto :eof
call wGetUrl2fil.cmd %wUrl% %wFil%
:: проблема?
if %wGetErr% == 1 set errInf=no %wUrl%
if %wGetErr% == 3 set errInf=no web{SI}
goto :eof

:scan01 выченение кличевых значений из первой страницы рейтинга aHits
::  pMax - общее число страниц
set pMax=
::  hMax - хиты топового раздела /лидер/
set hMax=
::  cDat - СИ-шная дата рейтинга
set cDat=
set tmp0=
for /F "tokens=1-9,*" %%J in (
  ' gawk.exe "{if(match($0,/\.shtml>([0-9]+)<\/a> *<\/center><DL><b>([0-9]+)</,a)){print(a[1],a[2]);exit}}" %fil1% '
) do (
  set pMax=%%J
  set hMax=%%K
)
for /F "tokens=1-9,*" %%J in (
  ' gawk.exe "{if(match($0,/ ([0-3][0-9]\/[01][0-9]):<\/b>/,a)){print(a[1]);exit}}" %fil1% '
) do set cDat=%%J
if NOT defined pMax set tmp0=NO pMax
if NOT defined cDat set tmp0=NO cDat %tmp0%
if defined tmp0 set errInf=%tmp0% in %fil1%
goto :eof

===v=== комплект п/п для обустройства заданных ожиданий в CMD
:waitSecs %%A организовать паузу в заданное число секунд /%1/ оживляж с новой строки, если /%2/
for /f "tokens=1-9,*" %%Q in (
  ' gawk.exe "BEGIN{t=systime();print(t,t+%1);exit}" '
) do (
  set jSec=%%Q
  set eSec=%%R
)
if NOT %2. == . echo.
:: начальная фиксация момента старта
call :ticSet
goto sleep6

:ticShow оживляж - это наше всё
gawk.exe "BEGIN{if(%curM%==%preM%){printf(\".\")}else{printf(%curM%)}exit}"
:: строка терминируется 10-й минутой /"0"/
if %curM% == 0 if NOT %curM% == %preM% echo. %curT%
:: проверка истечения заданного срока; "да"==вЫход
if %curS% GEQ %eSec% (
  if NOT %curM% == 0 echo. %curT%
  goto :eof
)
:sleep6  шаг задержки - 6 секунд
sleep 6
:: рабочая фиксация очередного момента
call :ticSet
goto ticShow

:ticSet запомним старое значение момента времени относительно начального /jSec/
set preM=%curM%
for /f "tokens=1-9,*" %%P in (
  ' gawk.exe "BEGIN{t=systime();print(t,strftime(\"%%H:%%M %%M\",t-%jSec%,1));exit}" '
) do (
  set curS=%%P
  set curT=%%Q
  set curM=%%R
)
:: и текущее /новое/ значение минут /последняя цифра/
set curM=%curM:~1%
goto :eof
===^=== комплект п/п для обустройства заданных ожиданий в CMD

:: 2018.08.05, nvv, creation
:: ...
:: 2019.11.29, nvv, samlib.ru budclub.ru - основной и альтернативный адреса "Самиздата"
::     + переменная nJob блокирует пилотную команду @echo off
:: 2019.12.06, nvv, current unification
:: 2020.03.13, nvv, улучшайзинг послегка-с...
:: 2020.04.06, nvv, улучшен алгоритм распознавания свежека, ЕСЛИ дата та же, а данные - свежие
:: 2020.04.29, nvv, minor corrections
:: 2020.05.23, nvv, set pGet=115 -> 120; set oPag=990 -> 999
:: 2020.05.23, nvv, set pGet=200
:: 2020.07.29, nvv, set pGet=250
:: 2020.09.12, nvv, set pGet=300 + отказ от отделения брака, всё вместе + у брака sizN==0
:: 2020.11.13, nvv, будКлуб - глупость
:: 2020.11.16, nvv, будКлуб - глупость? не факт... ;(
:: 2020.11.28, nvv, будКлуб - отнюдь... + мелкая косметика
:: 2021.05.11, nvv, очередная оптимизация с унификацией...
:: 2022.02.04, nvv, мелкий улучшайзинг оживляжа
:: 2022.02.07, nvv, прикапывание первой страницы полного рейтинга в U:\Jobs\#aHits\2022.hist.aHits 
:: 2022.02.16, nvv, мелкое улучцение оживляжа
:: 2022.06.07, nvv, мелкое улучшение оживляжа и логики + возможность возобновления после падения сети
:: 2023.07.11, nvv, пауза между запросами свежака в СИ через sleep, а не через цикл в gawk
:: 2023.07.22, nvv, поддержка включения "потеряшек" add2 в общий список aHits

    Имя файла скрипта: "bStat.awk" V-текст скрипта-V
- - - - - - - - -
BEGIN{                      #  bStat.awk scan+check *.stat.htm +mkLst книг-кандидатов в siTopTexts
# 2023.06.22, nvv, нет в aHits(?), возьмём прямо из stat
# ! история изменений - в подвале
# (!) для СКРЫТЫХ разделов нужны только эти данные:
#  из stat aHitP == 201661 p12m хиты за 12мес.
#  из stat aNamP == "Кадавр"
#  "0" (!) aNumP == 21 pNum место в aHits
  minT=200    #  минимальное кол-во хитов текста "за 30 дней", дабы "прорехи штопать" (если есть;)
  hitL=0      #  нижняя граница хитов "12мес" для отсечения от учёта неуспешных текстов и разделов
  nHit=0                                          #  число учитываемых топовых разделов в aHits
  gisF=1                                          #  флаг "нужны гистограммы"
  lowF=0                                          #  флаг "нужен отсев по хитам"
  nTxt=0                                          #  кол-во отобранных текстов
  tNum=0                                          #  число принятых ГОДНЫХ текстов
  hitT=0                                          #  сумма хитов "за 30 дней"
  ripN=0                                          #  число пустых суток (там же)
  ripP=0                                          #  число разделов-кандидатов на "штопку"
  endH=0                                          #  последний НЕ ноль "за 30 дней"
  rip1=0                                          #  первый ноль (там же)
  hitY=0                                          #  хиты произведения за "12мес"
  mifN=0                                          #  счётчик призраков СИ (текстов нет, хиты есть)
  nErr=0                                          #  счётчик проблемных файлов
  nGis=0                                          #  счётчик строк в файле с гистограммами
  nLow=0                                          #  счётчик файлов c низкими хитами
  dEnd="2000.01.01"                           #  макс.дата: 'datJ' (самая последняя - оперативная)
  dPre="2000.01.01"                               #  макс.дата: 'datJ' (предыдущая - "рассчётная"
  errS="] step.0 (NO file)"
  if(ARGV[1]=="")exit                             #  защита от случайного пуска

# объявим массивыы для общего использования в подпрограммах
  split("",aDatI)                                 #  чьи-с ;) -v- max хиты
  split("",aDatM)                                 #  макс хиты каждой найденной пары дат
  split("",aDatN)                                 #  счётчики пар дат: "fresh"-"datJ"
  split("",aHitP)                                 #  хиты раздела за 12мес. (из aHits)
  split("",aNamP)                                 #  ник автора в aHits
  split("",aNumP)                                 #  место раздела в aHits
  split("",aErr)                                  #  описания замеченных проблем со статистиками
  split("",aLow)              #  перечень отсеянных разделов (мало хитов за 12мес или за 30 дней)
  split("",aSel)    #  в ячейках статистики: url,title,hits:all,12mon,mon0..monC-11,day0..day0-61
  split("",aMig)                                  #  список timeStamp's статистик
  split("",aTxt)                                  #  перечень принятых текстов
  split("",aGis)                                  #  перечень принятых текстов
  split("",arr0)                                  #  рабочий массив
  split("",arr1)                                  #  рабочий массив

  mark=""                                         #  max dateTime statRefresh
  for(str2 in ENVIRON){                     #  ! регистр букв в имени переменной может быть любой
    str1=tolower(str2)                            #  Temp и temp - РАЗНЫЕ имена для gAwk...
    if(str1=="fhit")fHit=ENVIRON[str2]            #  опорный aHits список
    if(str1=="mint")minT=ENVIRON[str2]      #  минимальные допустимые хиты текста (меньше - брак)
    if(str1=="hitl")hitL=ENVIRON[str2]            #  нижняя граница хитов "12мес" (для отсечения)
    if(str1=="wdir")wDir=ENVIRON[str2]"\\"        #  место для всех ВЫХ.списков
    if(str1=="topn")topN=ENVIRON[str2]            #  минимальное кол-во топовых разделов из aHits
  }
  if(fHit==""){                                   #  если вызов НЕ из CMD, aHits ищем сами
    str0="U:\\Jobs\\_siTop.#"
    while((getline line<str0)>0){       #  ищем последнюю запись успешного скачивания этого списка
# ;1A hits 5:xNam 457:xNum 104555:xPas 2023.06.15:hDat 505501:hMax 1051:hPag 1111:dnLd 0.08~ratW 2023.06.16:done 05:22:08~wrks 14:47 
      if(match(line,/^;1A .+ 20([\.0-9]{8}):hDat /,arr0))fHit=arr0[1]
    }
    close(str0)
    if(fHit==""){
      errS="] step.0 NO *.aHits found in "str0
      exit
    }
    fHit="U:\\Jobs\\20"fHit".aHits"               #  строим полное имя нужного списка
    topN=30000
#     topN=200
  }
#   fRpt=wDir".lastInfo.bStat.#"                    #  итоговый отчёт-сводка
  fErr=wDir".upDatBad.bStat.#"                  #  список проблем и проблемных статистик (если-с;)
  fGis=wDir".upDatGis.bStat.#"              #  гистограммы посуточных хитов разделов с "прорехами"
  fLow=wDir".upDatLow.bStat.#"              #  мало хитов за "12мес." или за "30дн." (для порядка)
# 201661   21  846    3 k/kadawr "Кадавр" "no•comments"
# ;  hitP numP sizK sizN id_P     "namP"   "moto"
#      1    2    3    4 5         6        7
  print("? load "fHit,"...")                      #  заполним массив атрибутов разделов в *.aHits
  int1=0
  while((getline line<fHit)>0){                   #  fHit: like 2020.03.01.aHits
    split(line,arr0)                              #  arr0[5] == k/kadawr p_id
    if(match(arr0[1],/^[0-9]+$/)){
      aHitP[arr0[5]]=arr0[1]                      #  hitP arr0[1] == 201661 p12m хиты за 12мес.
      aNumP[arr0[5]]=arr0[2]                      #  numP arr0[2] == 21 место в aHits
      aNamP[arr0[5]]=arr0[6]                      #  namP arr0[6] == "Кадавр"
      int1++
      if(int1==topN)hitL=arr0[1]                  #  мин. достаточные хиты текста
      if((!nHit)&&(hitL-arr0[1]>0))nHit=int1-1    #  число учитываемых топовых разделов в aHits
      if((arr0[4])&&(!nHit))r2do++                #  должное число непустых разделов в $bStat
    }else if(match(line,/^;1A .+ 20([\.0-9]{8}):done /,arr0)){
      datH=arr0[1]                                #  дата скачивания aHits
      infH=line                                   #  для итогового списка отобранных текстов
    }
  }
  close(fHit)                                     #  всё/не_всё, - старый список тогось
# # errS=r2do":r2do"
# exit
  int7=(r2do-(r2do%100))/100
  if(r2do%100)int7=int7"+"
  if(int1==0){
    errS="] step.0 (no or empty %fHit%: "fHit " )"
    exit
  }
# массив имён месяцев для преобразования их в числа с лидирующим нулём 01..12
  mNum["Jan"]="01"                                #  .1 January . Jan. 31 . winter wint
  mNum["Feb"]="02"                                #  .2 February .Feb. 28/29
  mNum["Mar"]="03"                                #  .3 March . . Mar. 31 . spring sprn
  mNum["Apr"]="04"                                #  .4 April . . Apr. 30
  mNum["May"]="05"                                #  .5 May . . . May. 31
  mNum["Jun"]="06"                                #  .6 June . . .Jun. 30 . summer summ
  mNum["Jul"]="07"                                #  .7 July . . .Jul. 31
  mNum["Aug"]="08"                                #  .8 August . .Aug. 31
  mNum["Sep"]="09"                                #  .9 September Sep. 30 . autumn autm
  mNum["Oct"]="10"                                #  10 October . Oct. 31
  mNum["Nov"]="11"                                #  11 November .Nov. 30
  mNum["Dec"]="12"                                #  12 December .Dec. 31 . winter wint
  errS=""
  print("= loaded; nHit:"nHit"."hitL" minT:"minT) #  всё готово, начинаем сканирование статистик
  tIni=systime()
}

{                                                 #  // -=- основной блок скрипта begs
  if(FNR==1){         #  секция завершения работ с одним файлом и подготовки к работе со следущим
    if(NR!=1)chkSteps()                           #  чем закончилась обработка предыдущего файла?
    step=0        #  0-владелец, 1-дата обновления, 2-id раздела, 3-разбор строк таблиц статистики
    fPth=FILENAME
    fNam=split(fPth,arr0,"\\")                    #  NEW/NEXT входной файл as-is (с полным путём)
    fNam=split(arr0[fNam],arr0,".")               #  файл: чистое nam.ext (like "0~0.stat.htm")
    fNam=arr0[1]                                #  fNam чистое имя (like "0~0" in "0~0.stat.htm")
    id_P=substr(fNam,1,1)"/"substr(fNam,3)
    if(id_P in aHitP){                            #  этот раздел ЕСТЬ в рейтинге "по хитам"
      numP=aNumP[id_P]
      hitP=aHitP[id_P]
      namP=aNamP[id_P]
    }else{
      numP=0
      hitP=999                                    #  выясняется в stat
      namP="\""id_P"\""                           #  - "" - - "" -
    }
    inTab=0                                       #  сначала - мы НЕ "в таблице"
    numT=0                                        #  счётчик таблиц в файле статистики
    numR=0                                        #  счётчик строк в текущей таблице
    selN=0                                        #  счётчик ячеекк в строке таблицы
    int8++
# if(int8%100==0) print(sprintf(": %4s.%-4s",int8/100,int7),strftime("%H:%M:%S",systime()-tIni,-1))
    if(int8%100==0) print(sprintf(": %4s.%-4s",int8/100,int7))
    else if(int8%10)printf(".")
    else            printf(":")                   #  оживляж...
  }
  gsub(/(&nbsp;)+|\xA0+|[\0-\31]+/," ",$0) #  цепочки "&nbsp;" и прочих "пробелов" -> " " (пробел)
  if(step==0) {                                   #  ищем (для контроля) ФИО автора
# <h3>Статистика раздела &quot;<a href=./>Влад</a>&quot;:</h3>
    if (match($0,/<h3>Статистика раздела \&quot;<a href=\.\/>(.*)<\/a>\&quot;:<\/h3>/,arr0)) {
      if(arr0[1]==""){
        errS="] step."step" (NO nick in: " fNam " )"
        nextfile
      }else namP=pack(arr0[1])
      step++                                      #  проверка ОК
    }
  }else if(step==1){                              #  вычленеие даты обновления статистики
    if(match($0,\
/<li><i>Статистика рассчитывается .+\. \(.+ (...) +([0-9]+) (..:..:..) (20[0-9][0-9])\)<\/i>/\
,arr0)){
      if(length(arr0[2])==1) arr0[2]="0"arr0[2]   #  причешем первые дни месяца "1" -> "01"
      ticS=mktime(arr0[4]" "mNum[arr0[1]]" "arr0[2]" 12 00 00") # тики на 12 часов даты статистики
      datS=arr0[4]"."mNum[arr0[1]]"."arr0[2]      #  дата обновления статистики
      timS=arr0[3]                                #  время обновления статистики
      if(mark<datS"/"timS)mark=datS"/"timS
      step++                                      #  дату статистики оптичили
    }
  }else if(step==2){            #  ищем authID (!должен быть и совпадать с префиксом имени файла)
# [<a href=http://top.mail.ru/pages?id=77427&period=0&date=2018-10-02&filter_type=0
# &filter=n/nosow_w_w&pp=20&gender=0&agegroup=0>по дням</a>]
    if (match($0,/\[<a href=http.+&filter=([0-9a-z]\/[\-0-9a-z_]+)&pp=.+>по дням<\/a>]/,arr0)) {
      if(id_P!=arr0[1]){                          #  это ПОЛНЫЙ криминал!
        errS="] step."step" ("arr0[1]" != fNam: " fNam " )"
        nextfile
      }
      step++                                      #  проверка ОК
    }
  }else if(step==3){                #  таблицы статистики, просмотр строк, сборка данных в ячейках
    if($0=="<!--------- Подножие ------------------------------->"){  #  разбор закончен
      step++
      nextfile
    }
    if(inTab){                      #  Мы ВНУТРИ таблицы, собираем строки таблицы с хитами текстов
      buf0=buf0" "$0                              #  gain inpLines to buf0, then check contents
      while((match(buf0,/<tr ?[^>]*>/))||(int0=index(buf0,"</tr"))){ # seek <tr .. </tr for tabRow
        if(RSTART){                               #  new/next line begs
          numR++                                  #  счётчик строк
          buf0=substr(buf0,RSTART+RLENGTH)  #  del всё до начала строки таблицы <tr>, в т.ч. и его
        }
        if(int0=index(buf0,"</tr")){              #  this line ends
          getSels(substr(buf0,1,int0-1))          #  tabl_row -> arrow:sell_values
          buf0=substr(buf0,int0+4)              #  убрали эту строку из буфера и окультуриваем...
          id_T=aSel[0]                        #  имя+расшир файла с текстом (или "./" для раздела)
          namT=aSel[1]                    #  пока фактическое (as-is) название текста в статистике
          if((numT==1)&&(numR==2)){               #  хиты по разделу (спец.обслуживание)
            if(!numP)hitP=aSel[3]                 #  хиты раздела за "12мес" ЕСЛИ нет в aHits
            aMig[++nMig]=datS"_"timS" "datJ sprintf("%6s%8s %s %s %s"\
,numP,hitP,fNam,id_P,namP)                        #  момент обновления
            if((hitP-hitL<0)||(hitT-minT<0)){     #  в разделе МАЛО "за 12мес" ИЛИ "за 30 дней"
              nLow++                              #  и сосчтиаем...
              aLow[nLow]=sprintf("%6s%8s%8s%8s %s %s"\
,numP,hitP,hitP,hitT,id_P,namP)" "fNam
              step=4                              #  т.е. этот раздел сейчас НЕ интересен
              nextfile
            }
            if((hitT-minT>0)&&(rip1!=0)&&((endH-rip1>0)||rip1==1)){
              ripP++                              #  число разделов-кандидатов на "штопку"
              id_T="~"                            #  prt 8-раздел: 'хиты за 30 дней'
              namT="\"<!>\""
              selsOutG()                          #  гистограмма: хитов много + есть "прореха"
            }
          }else{                                  #  обычная строка статистики текста
            int0=split(id_T,arr0,".")
            if(     int0!=2){}                #  sErr="zOrg" нарушен формат имени файла: name.ext
            else if(arr0[2]!="shtml"){}           #  sErr="xExt" расширенеие не то-с
            else if(gensub(/[\-0-9a-z_]/,"","g",arr0[1])!=""){} # sErr="vNam" левизна в имени файла
            else if(namT==""){}                   #  sErr="tHdr" "пустое" название текста
            else if(minT-hitT>0){}                #  sErr="hLow" хитов таки мало
            else {                #  0-"всё ОК" поток строк с текстами и хитами для выбора top1000
              namT=pack(namT)                     #  запакованное (свёрнутое!) название текста
              if(match(namT,/^""$/)){
                namT=hitT":noNamT"
                hitT=2
              }else{
                tNum++                            #  число принятых ГОДНЫХ текстов
              }
              aTxt[++nTxt]=sprintf("%7s%8s%6s",hitT,hitP,numP)" "id_P\
" "arr0[1]" "namP" "namT" "fNam" "datJ            #  строка для итогового списка текстов
              if(hitT-aDatM[dGap]>0){             #  максимум - только у принятых!
                aDatM[dGap]=hitT                  #  <- max хиты, v- чьи-с ;)
                aDatI[dGap]=sprintf("%7s %5s %6s",hitP,numP,hitT)" "id_P" "arr0[1]
              }
            }
            if(minT-hitY>0){                      #  хиты за "12мес", дальше - хитов ещё меньше
              step=4
              nextfile
            }
          }
        }
      }
      if(int0=index(buf0,"</table")){             #  this tabl ends
        buf0=substr(buf0,int0+4)                  #  update buf0
        inTab=0
      }
    }else{                                        #  ищем начало таблицы
      if(int0=index($0\
,"<table border=1 cellspacing=0 cellpadding=0><tr><td><td colspan=2><b>Итого</b></td>")){
        numT++                                    #  new table
        numR=1                #  певая строка каждой таблицы - календарь хитов статистики раздела
        buf0=substr($0,int0+80)
        if(numT==1){                              #  1-st table 1-st row -> get col-dates & so-on
          match(buf0,/<tr[^>]*>(.+)<\/tr[^>]*>/,arr0) #  собственно строка с календарём
          getSels(arr0[1])                        #  но самую первую такую - разберём-с
          tic0=ticS           #  и привяжем дату к нулевому (самому левому) дню посуточных данных
          while(strftime("%d",tic0)!=aSel[16])tic0=tic0-86400 #  сдвинем стат.дату к нулевому дню
          datJ=strftime("%Y.%m.%d",tic0-86400)    #  полная дата первого дня в интервале 30 дней
          dGap=datS"-"datJ
          aDatN[dGap]++                           #  счётчики "datJ" дат в статистиках
          if( dEnd<datJ)               dEnd=datJ  #  оперативная 'datJ'
          if((dPre<datJ)&&(datJ<dEnd))dPre=datJ   #  предыдущая 'datJ'
        }
        inTab=1                                   #  мы ВНУТРИ таблицы
        buf0=""                                   #  буфер чист!
      }
    }
  }
}                                                 #  // -=- основной блок скрипта ends

function getSels(rowLn){                #  разделение строки таблицы статистики на массив значений
  hitT=0                                          #  + сумма хитов "за 30 дней"
  ripN=0                                          #  число пустых суток (там же)
  endH=0                                          #  последний НЕ ноль "за 30 дней"
  rip1=0                                          #  первый ноль (там же)
  selN=0                                          #  sells count
  split("",aSel)
  aSel[0]=""
  while(match(rowLn,/<td[^>]*>/)){                #  это объявление ячейки
    selN++                  #  0.1-имя/ссылка; хиты: 2-всего, 3-12мес, 4..15-заМес, 16..77-заСутки
    rowLn=substr(rowLn,RSTART+RLENGTH)            #  выкусим объявление ячейки
    f3n2=index(rowLn,"</td")                      #  это конец ячейки
    f3s2=substr(rowLn,1,f3n2-1)               #  это сама ячейка (с возможными внутренними тегами)
    if(selN==1){                            #  в ПЕРВОЙ ячейке - название текста и ссылка на него
      match(f3s2,/<a href=([^>]+)>(.*)<\/a>/,f3a1)  #  выцепим и разложим:
      aSel[1]=f3a1[2]                             #  название текста
      aSel[0]=f3a1[1]                             #  ссылка на текст: name.shtml
    }else aSel[selN]=rmTags(f3s2) #  в ПРОЧИХ ячейках - только значение ячейки, очищенное от тегов
    rowLn=substr(rowLn,f3n2)                      #  выкусим оптиченную ячейку
    if((selN-16>0)&&(47-selN>0)){
      hitT=hitT+aSel[selN]                        #  сразу подсчитаем сумму хитов "за 30 дней"
      if(aSel[selN]==0){                          #  и уточним важные подробности
        ripN++                                    #  + число пустых суток (там же)
        if(rip1==0)rip1=selN-16                   #  первый ноль "за 30 дней"
      }else        endH=selN-16                   #  последний НЕ ноль (там же)
    }
  }
  hitY=aSel[3]                                    #  хиты произведения за "12мес"
  if(selN==77)return                              #  row HAVE had 77 sells
  nextfile                                        #  битая таблица == битый файл
}

function rmTags(f2s1){                            #  удаление ВСЕХ тегов в литерале
  while(match(f2s1,/<\/?[a-zA-Z][1-6a-zA-Z]?[a-zA-Z]*[ \/]?[^>]*>/))
      f2s1=substr(f2s1,1,RSTART-1) substr(f2s1,RSTART+RLENGTH)
  return(f2s1)
}

function selsOutG() { # отладка: на StdOut гистограмма значений массива aSel +маркировка "прорехи"
  int0=0                                      #  +с посуточной шкалой +справочные сведения касаемо
  hSum=0
  for(int1=17;int1<47;int1++){
    if(aSel[int1]-int0>0)int0=aSel[int1]          #  ищем максимальные хиты за сутки
    hSum+=aSel[int1]                              #  +фактическию сумму хитов "за 30 дней"
  }
  int0=length(int0"")
  ++nGis
  split("",arr1)                                  #  готовим (собираем гистаграмму)
  for(int1=1;int1<31;int1++){
    str0=sprintf("%"int0"s",aSel[int1+16])
    for(int2=1;int2<=int0;int2++){                #  в виде горизонтальных строк цифр
      str2=substr(str0,int2,1)
      if((int2==int0)&&(aSel[int1+16]==0))str2="_"  #  ЭТО суточная "прореха" (0 хитов)
      arr1[int2]=arr1[int2] str2
    }
  }
  str0=strftime("%d",tic0-86400) "  "             #  строим шкалу времени; привязанную к границам
  for(int2=5;int2<30;int2+=5)str0=str0 strftime("%d",tic0-86400*int2) "   "
  str0=str0 strftime("%d.%m.%Y",tic0-86400*30)" hSum="hSum
  aGis[++nGis]=str0                               #  оформляем всё от...
#        |19 15   10   05   31   26   21.01.2020  # пример посуточной шкалы времени
  aGis[++nGis]="v...:....v....:....v....:....v"
#        31302922396567855______0629939   # пример самой нижней строки гистограммы (с "прорехой")
  for(int2=1;int2<=int0;int2++)aGis[++nGis]=arr1[int2]
  aGis[++nGis]="|...:....|....:....|....:....|"
  aGis[++nGis]=id_P"/"id_T" "namT" " fNam         #  ... и до
}

function pack(f1s1) {       #  мешающие chars -> нейтральные + cut too long + закавычить результат
  f1n1=split(f1s1,f1a1)
  f1s1=f1a1[1]                                    #  обихаживание пробелов... " " -> "•"
  for(f1n2=2;f1n2<=f1n1;f1n2++)f1s1=f1s1"•"f1a1[f1n2]
  f1s2=""                                         #  двойные кавычки -> "&quot;"
  while(f1n1=index(f1s1,"\"")){f1s2=f1s2 substr(f1s1,1,f1n1-1)"&quot;"; f1s1=substr(f1s1,f1n1+1)}
  f1s1=f1s2 f1s1
  f1s2=""                                         #  "%" -> "&#x25;"
  while(f1n1=index(f1s1,"%")){f1s2=f1s2 substr(f1s1,1,f1n1-1)"&#x25;"; f1s1=substr(f1s1,f1n1+1)}
  f1s1=f1s2 f1s1
  if(length(f1s1)>999){                           #  обрезка слишком длинных литералов
    f1n1=index(substr(f1s1,1000),"•")
    if(!f1n1)f1n1=1000
    else   f1n1=f1n1+999                          #  v-вместо обрезанного - справка о факте ;)
    f1s1=substr(f1s1,1,f1n1-1)"•(+"(length(f1s1)-f1n1)" chars)"
  }
  return ("\""f1s1"\"")                           #  результат "закавычивается!"
}

function chkSteps() {                     #  проверка чем закончилась обработка очередного файла?
  if((step==4)&&(errS=="")){                      #  всё штатно ("ОК")
    return
  }else{
    if(errS==""){                                 #  что-то не так, но нужно уточнить...
      if(step==3){
        if(inTab==1){                             #  уточним проблему...
          if(selN!=77){                           #  row HAVE had 77 sells
        errS="] step."step" (selN:"selN" Tab:"numT" Row:"numR"{not 77}): " fNam " )"
          }else if((numT==1)&&(numR==2)){
        errS="] step."step" (bad 1-st table 2-nd row in: "    fNam " )"
          }else{
        errS="] step."step" (unClosed table in: "             fNam " )"
          }
        }else{
        errS="] step."step" (no or bad table in: "            fNam " )"
        }
      }else if(step==2){
        errS="] step."step" (no or bad author's ID in: "      fNam " )"
      }else if(step==1){
        errS="] step."step" (no or bad refresh date:{"$0"}: " fNam " )"
      }else if(step==0){
        errS="] step."step" (no partition`s owner in: "       fNam " )"
      }
    }
    aErr[++nErr]=errS
    aMig[++nMig]="0000.00.00_00:00:00 _bad_file_"sprintf("%6s%8s %s %s %s"\
,numP,hitP,fNam,id_P,namP)                        #  этот файл "на вынос"
    aDatN["0000.00.00-_bad_file_"]++
# 2021.03.05-14:21:54 2021.03.04 29076     514 a/ass "Асс"
# aBad[nErr]=datS"-"timS" "datJ sprintf("%6s%8s %s %s"\
# ,numP,hitP,id_P,namP)   # заполняем массив моментов обновления статистик
    errS=""                                       #  и очистим флаг-инфо
  }
}

function mkRpt1(ln2rpt) {                   #  дабы нужное отправлять и в отчёт, и на экран, и...
  print(ln2rpt)                                                         >fLst
  print(ln2rpt)                                                         >fMig
#   print(ln2rpt)                                                         >fRpt
  print(ln2rpt)
}

function mkRpt2(ln2rpt) {                         #  дабы правильно отправлять нужное куда след...
  print("; "ln2rpt)                                                     >fMig
#   print(ln2rpt)                                                         >fRpt
  print(ln2rpt)
}

END{                                              #  отстрелялись; ну почти...
  if(errS!=""){
    print("")
    print(errS)                                   #  есть проблем-с => подудим
    exit 1                                        #  и на выход с вещами
  }
  chkSteps()                                      #  чем закончилась обработка последнего файла?
  if(errS!=""){
    print("")
    print(errS)                                   #  есть проблем-с => подудим
    exit 1                                        #  и на выход с вещами
  }

  if(int8%100)print("")
  print(" == " nMig,strftime("%H:%M:%S"),strftime("~%H:%M:%S~",systime()-tIni,-1))  #  проблем нет

  asort(aLow)                 #  перечень отсеянных разделов (мало хитов за 12мес или за 30 дней)
  asort(aMig)                                     #  подготовка списка дата/времы обновления day01
  asort(aTxt)             #  подготовка (прямая сортировка) и выдача (в обратном порядке!) списка

  split("",aSel)                                  #  сборка итогового отчёта
  split(aMig[nMig],arr0)
  keep=" = "
  sEnd=substr(arr0[1],1,10)                       #  дата самого последного обновления статистик
  dEnd=arr0[2]                                    #  последний datJ (верхняя граница расчёта)
  hEnd=aDatM[str0]                                #  макс. хиты текста за datJ
  nEnd=1                                          #  число статистик с datJ
  hPre=0                                          #  макс. хиты текста ВНЕ datJ
  nPre=0                                          #  число статистик ВНЕ datJ
  nSel=1
  str0=sEnd"-"dEnd                                #  самая свежая (последняя) пара дат datS-datJ
  aSel[nSel]=str0 sprintf("%6s %4.1f%",aDatN[str0],aDatN[str0]*100/nMig) keep aDatI[str0]
  frst=0                                    #  первая строка итоговой сводки с "xDel/=Keep" == "="

  for(int1=nMig-1;int1>0;int1--){                 #  сканируем aMig собирая отчёт
    split(aMig[int1],arr0)
    arr0[1]=substr(arr0[1],1,10)
    if(str0!=arr0[1]"-"arr0[2]){                  #  перешли границу суток в датах обновления
      if(!frst&&(arr0[2]!=dEnd))frst=nSel         #  у этой строки возможно будет смена "=" на "x"
      nSel++
      str0=arr0[1]"-"arr0[2]                      #  новая пара дат datS-datJ
      if(aDatI[str0]==""){                        #  если в ней нет значимых текстов, то...
        aDatI[str0]="      ~     ~      ~ ~    ~" #  отметим факт
        keep=" x "                                #  и назначение: "на удаление"
      }else if(arr0[2]!=dEnd)keep=" x "           #  и назначение: "на удаление"
      aSel[nSel]=str0 sprintf("%6s %4.1f%",aDatN[str0],aDatN[str0]*100/nMig) keep aDatI[str0]
    }
    if(arr0[2]==dEnd){
      if(hEnd-aDatM[str0]<0)hEnd=aDatM[str0]      #  макс. хиты текста за datJ
      nEnd++                                      #  число статистик с datJ
    }else{
      if(aDatM[str0]&&(hPre-aDatM[str0]<0))hPre=aDatM[str0] #  макс. хиты текста ВНЕ datJ
      nPre++                                      #  число статистик ВНЕ datJ
    }
  }

  if(hitL-hPre>0){                            #  в Pre НЕТ хитового текста => рейтинг собирается!
    split(aSel[frst],arr0,"% = ")                 #  у этой строки НУЖНО сменить "="...
    aSel[frst]=arr0[1]"% x "arr0[2]               #  ... на "x"
    if(datH==strftime("%y.%m.%d"))vOk="v"
    else                          vOk="~"
    preF=wDir dEnd "." vOk
    vOk="mod:"vOk
  }else{                                          #  иначе - увы...
    preF=wDir dEnd ".!"                           #  собирать НЕЛЬЗЯ!
    vOk="mod:!"
  }
  fLst=preF".lst.bStat"                           #  итоговый список текстов-кандидатов siTopTexts
  fMig=preF".mig.bStat"                           #  сортированные моменты обновления статистик

  if(nErr)for(int1=1;int1<=nErr;int1++)print(aErr[int1])               >>fErr
  if(gisF){                                       #  заказана гистограмма, выдадим, что нашлось
    if(nGis)for(int1=1;int1<=nGis;int1++)print(aGis[int1])             >fGis
    else                                 print("")                     >fGis
  }

  if(lowF){                                       #  флаг "нужен отсев по хитам"
    print("; NumP.1  HitP.2  h12m.3 hitT.4 id_P.5 \"NamP\".6 fNam.7")   >fLow
    for(int1=1;int1<=nLow;int1++)print(aLow[int1])                      >fLow
    print("; nHit:"nHit"."hitL" nLow:"nLow" done:"strftime("%Y.%m.%d,%H:%M")) >fLow
  }

  for(int1=nTxt;int1>0;int1--)print(aTxt[int1])                         >fLst #  ОТОБРАННЫЕ тексты
# . . . . . 9560  365732     6 k/kadawr n "Кадавр" "Псион" k~kadawr 2021.04.03
  print("; hitT.1 hitP.2 numP.3 id_P.4 id_T.5 \"namP\".6 \"namT\".7 id_F.8 datJ.9") >fLst

  mkRpt2( "upDat_datS  1-st_day   numS xDel/=Keep hitP  numP   hitT id_P id_T")
  for(int1=nSel;int1>0;int1--)mkRpt2(aSel[int1])
  print(";     datS_timS.1     datJ.2  numP.3  hitP.4 id_F.5 id_P.6 \"namP\".7") >fMig
  for(int1=1;int1<=nMig;int1++)print(aMig[int1])                        >fMig
#        2021.04.04_00:22:48 2021.04.03   186   15887 a~arh a/arh "Арх"

# upDat_datS  1-st_day   numS xDel/=Keep hitP  numP   hitT id_P id_T
# 2021.04.17-2021.04.16     4  0.0% x       ~     ~      ~ ~    ~
# 2021.04.18-2021.04.17     3  0.0% x       ~     ~      ~ ~    ~
# 2021.04.21-2021.04.20  1985  6.6% x  200017    14  20113 z/zhan flibustain
# 2021.04.22-2021.04.21 19691 65.7% =  791291     1  61567 m/metelxskij_n_a wtt-2
# 2021.04.23-2021.04.21  8266 27.6% =  241332    13  29405 r/raba_b_i molitva-113

  print(infH)                                                           >fLst
  print(infH)                                                           >fMig
#   print(infH)                                                           >fRpt
# ;2B stat 2021.04.26:sEnd 1457:nPre 20102:hPre 1457:miss 300:minT 548:hitL 30075:nHit last~01:01:31~
  mkRpt1(";2B stat"sprintf(" %s:sEnd %5s:nPre %6s:hPre %5s:miss"\
,sEnd,nPre,hPre,nMig-nEnd)" "minT":minT "hitL":hitL "nHit":nHit last~"substr(mark,12)"~")
# ;2C scan 2021.04.24:dEnd 28554:nEnd 60688:hEnd 30011:nMig 1592:tNum 10:nGis 29173:nLow mod:! OkV:95.1%
  str1=substr(sprintf("%1.1f",nEnd*100/nMig),1,4)
  mkRpt1(";2C scan"sprintf(" %s:dEnd %5s:nEnd %6s:hEnd %5s:nMig %s:tNum %s:nGis %s:nLow %s OkV:%s% "\
,dEnd,nEnd,hEnd,nMig,tNum,ripP,nLow,vOk,str1))

# 2020.02.20, nvv, первые прикидки "что и как НАДА делать-с"
# 2020.03.28, nvv, рабочий макет (будем посмотреть...)
# 2020.05.25, nvv, aHits - 120 pages-! => midS=8000 -> 9600; minS=9750 -> 11700
# 2020.06.25, nvv, minor updates
#   + это излишне:  fDel=wDir"]bStat.del" - список списков оставляемых статистик
# 2020.05.31, nvv, полнота нужной выборки вычисляются по % в наличных статистиках:
#   >97% - всё ОК (выборка достаточна для сборки рейтинга), на завтра - 1 последний список
#   >75% - 2-е сутки 3-х суточного такта, на завтра - 2 последних списка
#   <=75% - 1-е сутки, на завтра - 1 последний список
# 2020.07.16, nvv, битые статистики теперь просто фиксируются в "]bStat.bad",
#     не прерывая весь процесс + уточнён алгоритм разграничения списков: xDel/=Keep
# 2020.08.29, nvv, мелкая косметика
# 2020.09.07, nvv, имя файла *.aHits (если пуста переменная fHist) ищется в общем логе "_siTop.#"
# 2020.09.18, nvv, слегка перепахано для более тонкого разбора наличности
#   + итоговый отчёты см. переменную wDir
#   + итоговый fLst формируется сразу отсортированный и оформленный
#     + в конце строк с атрибутами отобранных текстов - datJ (1-й_полный_день в прошлое)
# 2020.10.07, nvv, переделано (упорядочено) накопление, оценка и оформление итогов по датам
#     (для оценки пригодности к рассчёту, см. dEnd и dPre)
# 2020.11.03, nvv, добавленена работа с хитами 12мес раздела
# 2020.11.07, nvv, исправлена мелкая неточность вчисления "недостающего" в итоговом отчёте
# 2021.01.09, nvv, исправлена сборка масива aMig (для fTim даты обновления и Ко)
#     + изменен учёт и оформление разделов с низкими хитами за "12мес." или за "30дн." fLow
# 2021.01.29, nvv, minor turning
# 2021.03.25, nvv, в итогах явно указывается % годных к расчёту статистик
# 2021.04.09, nvv, серьёзно переделано (логика работы и унификация)
# 2021.04.09, nvv, minor changes
# 2021.05.02, nvv, косметика средней тяжести... ;)
# 2021.06.18, nvv, иной формат *.mig.bStat
# 2022.02.17, nvv, мелкое улучшение оживляжа + устранена мелкая неточность логики
# 2022.04.13, nvv, для расчёта дата aHits /см. datH/ должна быть "сегодня" (== "v", иначе "~")
# 2022.05.02, nvv, сводка по *.!.mig.bStat теперь в шапке этого списка
# 2022.12.30, nvv, сменил разрешение экрана /меньше пикселей/, пришлось менять оживляж
# 2023.03.09, nvv, was: set minT=300, now: 200
}

    Имя файла скрипта: "bStat.cmd" V-текст скрипта-V
- - - - - - - - -
@if not defined nJob @echo off
rem 2023.07.28, nvv, сокращена выдача в общий лог jRpt
rem ! история изменений - в подвале

set jDat=%date%
rem  wDir - общее рабочее место
rem  topN - примерное число сканируемых топовых статистик
rem  zDir - место для сегодняшних итоговых списков
rem  0==1 - тестовый режим, 1==1 - рабочий
if 1 == 1 (
  set topN=30000
  set wDir=%~d0\Jobs\
  set zDir=%~d0\Stor\wOld\%jDat%\
) else (
  set topN=30000
rem   set topN=200
  set wDir=%~dp0Test\
  set zDir=%~dp0Stor\
)
set nJob=bStat
rem bStat.cmd - "обновлятель" статистик 30k разделов +список самых посещаемых за 30 дней произведений
rem ! обязателен список U:\Jobs\20??.??.??.aHits
rem ! обязателен файл принудительных уставок .delUntil.bStat.# {может быть пуст}
rem = *.lst.bStat - итоговый "черновой" список топовых текстов {2021.04.20.v.lst.bStat}
rem + *.mig.bStat - отсортированный по моментам список файлов статистик{2021.04.20.v.mig.bStat}
rem ~ .upDatBad.bStat.# - список проблемных разделов {в этот раз}
rem ~ .upDatGis.bStat.# - список разделов с прорехами в статистике

rem после запуска:
rem - если нет или пусто в #bStat - тупо скачивается всё по aHits
rem - если нет файла *.mig.bStat - строятся оба *.bStat {по #bStat\*.stat.htm}
rem иначе смотрится на наличие сегодняшнего файла %jDat%.upd.2do.2do {"надо сделать"}
rem   есть - значит была авария и следует продолжит с места, где прервали
rem   нет - смотрится расклад в: fHIt, fMig и в .delUntil.bStat.#
rem NB. ДО начала скачивания на экран выдаётся оный расклад и запрашивается явное "ОК" {"Y"}

rem OS windows - должны быть настроены системные переменные окружения:
rem %time% - HH:MM:SS,tt HH<=9 -> "HH" == " H" лидирующий пробел для часа меньше 10
rem %date% - YYYY.mm.dd

rem основные сеты и прочая подготовка инструментария...
echo %jDat% %time:~0,8% %~nx0 Begs
set errInf=
rem  siUrl - основной и резервный URL Самиздата {желательно работать с резервом}
set siUrl=samlib.ru
if exist \siUrl.budclub set siUrl=budclub.ru
rem  dnLd - счётчик /формально/ успешно скачанного {НЕ 0b}
set dnLd=0
rem  ratW - темп скачивания {секунд на один файл в среднем}
set ratW=0
rem  cNum - счётчик-индикатор числа скачиваемых статистик
set cNum=0
rem set cGap=1
rem  minT - минимальные допустимые хиты текста {меньше - брак/отсев/etc}
set minT=200
rem  jDir - место для оперативных файлов
set jDir=%wDir%#%nJob%\
rem  aChk - анализатор текущего расклада по спискам, выдаёт что следует: грузить/удалить/не_трогать
set aChk=%~dpn00.awk
rem  aScn - сканер статистик, строит черновой список топовых текстов {и *.mig.%nJob% в частности}
set aScn=%~dpn0.awk
rem  эти скрипты обязаны быть
for %%A in (%aChk% %aScn%) do if NOT exist %%A set errInf=NO %%A found & goto nExit
rem  jRpt - общий отчёт с итоговыми сводками процессов
set jRpt=%wDir%_siTop.#
rem  sNul счётчик "статистик недоступно" {скачано 0b}
set sNul=0
rem  wGetSiz - размер скачанного файла {default 1b минимум}
set wGetSiz=1
rem  wGetTot - размер скачанного файла {default 1b минимум}
rem set wGetTot=0
rem  bErr - оперативный {обнуляемый} файл для пометок CMD{скачивание} + AWK{сканирование}
set bErr=%wDir%.upDatBad.%nJob%.#
rem  f2do - список "НАДО сделать сегодня"
set f2do=%wDir%%jDat%.upd.2do.2do
rem  fDon - список "УЖЕ сделано сегодня"
set fDon=%wDir%%jDat%.upd.don.2do
rem  fDon - список "ПРОПУСКАЕТСЯ сегодня"
set fPas=%wDir%%jDat%.upd.pas.2do
rem  fSta - файлы статистики в #bStat
set fSta=%jDir%?~*.stat.htm
::  nSuf - файл статистики nvv /общая часть/
set nSuf=n~nosow_w_w.stat.htm
::  nFil - полное имя обновляемого файла статистики nvv
set nFil=%jDir%%nSuf%
::  nDat - последняя наличная дата статистики nvv в формате "yyyy.Mns.d" /like: "2022.Jul.3"/
set nDat=
::  nDir - склад для накопления nvv.stats /должен быть/
set nDir=%~d0Stor\nvv\
if NOT exist %nDir%*.* mkDir %nDir%
if errorLevel 1 set errInf=fail mkDir %nDir% & goto nExit
:: выясним последнюю дату обновления статистики nvv на складе для
for %%A in (%nDir%*%nSuf%) do set tmp0=%%A
if defined tmp0 (
  for /F "tokens=1-9,*" %%A in (
'gawk.exe "{if(match($0,/<li><i>.+\. \(... (...) (..) ..:..:.. (20..)\)<\/i>/,a)){print(a[3],a[1],a[2]);exit}}" %tmp0%'
  ) do set nDat=%%A.%%B.%%C
)
:: и обрадуем наблюдатеоя касаемо
echo nDat:%nDat%
if NOT exist %zDir%*.* mkDir %zDir%
if errorLevel 1 set errInf=fail mkDir %zDir% & goto nExit

rem НЕ сегодняшние 2do точно НЕ нужны!
for %%A in (%wDir%*.2do) do call :chkDel2do %%A
if defined errInf goto nExit

rem  fHit - ВХОДНОЙ список *.aHits {последний из и запомнить его имя} ОБЯЗАТЕЛЕН
set fHit=
for %%A in (%wDir%20??.??.??.aHits) do set fHit=%%A
if NOT defined fHit set errInf=20??.??.??.aHits NOT found & goto nExit
for /F %%A in ( 'gawk.exe "{if(NR==%topN%){print $1;exit}}" %fHit%' ) do set hitL=%%A
for /F %%A in ( 'gawk.exe "{if($1-%hitL%<0){print NR-1;exit}}" %fHit%' ) do set hitN=%%A
rem ;1A hits 4:xNam 449:xNum 102547:xPas 2022.02.06:hDat 572691:hMax 1030:hPag 1031:dnLd 0.20~ratW 2022.02.08:done 02:20:50~wrks 15:01
for /F "tokens=1-2,*" %%A in (
  'gawk.exe "{if(match($0,/^;1A .+ ([0-9]+):hMax .+ ([^ ]+):done /,a)){print(a[1],a[2]);exit}}" %fHit%'
) do (
  set hMax=%%A
  set hitD=%%B
)
rem  mark - префикс метки комлекта файлов 2do
set tmp0=%time:~0,5%
if "%time:~0,1%" == " " set tmp0=0%tmp0:~1%
set mark=;9N %tmp0%~ %hMax%:hMax %hitD%:hitD
rem э ключи fHit: hMax и hitD
echo * fHit:%fHit% - - - - %hitD%:hitD %hitN%:hitN %hitL%:hitL

rem fUnt - уставки границ "от-доНе" дата/время обновляемых статистик файл ДОЛЖЕН быть, уставки - нет
set fUnt=%wDir%.upDatSet.%nJob%.#
if NOT exist %fUnt% set errInf=%fUnt% NOT found & goto nExit

rem  fMig - отсортированный и размеченный список дат обновления статистик 2022.01.21.!.mig.bStat ЖЕЛАТЕЛЕН
set fMig=
set m2do=1
for %%A in (%wDir%20??.??.??.?.mig.%nJob%) do set fMig=%%A
rem ;2B stat 2022.02.08:sEnd   170:nPre      0:hPre   170:miss 300:minT 535:hitL 30035:nHit last~01:04:49~
rem updN - указатель: "обновить 10k" "обновить 'столько-то килобайт'", значения: "ПУСТО" или "10"
rem updB - дата-время обновления статистик, с которого скачиваются /обновляются/ копии с СИ
rem updE - дата-время обновления статистик, с которого прекращается скачивание свежих копий
if defined fMig (
  for /F "tokens=1-9,*" %%A in (
    'gawk.exe "{if(match($0,/^;2B .+ ([^ ]+):sEnd * ([0-9]+):nPre /,a)){print(a[1],a[2]);exit}}" %fMig%'
  ) do (
    set sEnd=%%A
    set nPre=%%B
  )
  for /F "tokens=1-9,*" %%A in (
    'gawk.exe -f %aChk%'
  ) do (
    set nPas=%%A
    set nDel=%%B
    set nUpd=%%C
    set nTot=%%D
    set updB=%%E
    set updE=%%F
    set updN=%%G
  )
  if "%nPas%" == "]" (
    set errInf=err in gAwk %m2do%:m2do %nDel% %nUpd% %nTot%
    goto nExit
  )
  if errorLevel 1 set errInf=err in gAwk %m2do%:m2do with %aChk% & goto nExit
  set tmp0=*
) else (set tmp0=X)
rem э ключи fMig: nPre и sEnd
set /a n2do=%nDel%+%nUpd%+0
echo %tmp0% fMig:%fMig% - %sEnd%:sEnd %nPre%:nPre %updB%updB %updE%updE %updN%updN

rem при первом за день старте f2do отсутствует, но нужно таки определиться с текущей ситуацией
set s2do=
set o2do=
set rDon=0
set r2do=0
rem если статистик ни одной, нужно всё прочее игнорировать и всё по fHit скачивать-с
if exist %fSta% (
  call :chkNow
  if defined errInf goto nExit
)
rem смотрим - не было ли аварии уже... если таки уже - надо продолжить...
rem [!] f2do{Mig}: 10:done 9239 , , k.:2do
if defined s2do (
  set j2do=reSume
  set jInf=RESUME dnLding
  echo [!] f2do{%s2do%}: %rDon%:done %r2do%:2do
  goto reqOK
)

rem смотрим если есть какие-нибудь статистики И  fMig
set tmp0=
if NOT "%updB%" ==  "," set tmp0=%tmp0% %updB%updB
if NOT "%updE%" ==  "," set tmp0=%tmp0% %updE%updE
if NOT "%updN%" == "k." set tmp0=%tmp0% %updN%updN
if exist %fSta% (
  if defined fMig (
    echo ; upDat_datS  1-st_day   numS xDel/=Keep hitP  numP   hitT id_P id_T
    gawk.exe "{if(match($1 $5,/^;[x=]$/))print($0)}" %fMig%
    if %n2do% == 0 (
      set j2do=nExit
      set jInf=NOTHING to do NOW: %nDel%:nDel %nUpd%;nUpd %n2do%:nTot
    ) else (
      set j2do=updMig
      set jInf=fresh:%tmp0% %nDel%:nDel %nUpd%;nUpd %n2do%:nTot
    )
  ) else (
    set j2do=creMig
    set jInf=scan ALL {~%hitN%}
  )
) else (
  set j2do=dlHist
  set jInf=DnLd ALL {~%hitN%}
)
rem echo ??? %j2do%
rem exit

rem возможно что и делать-то нечего, тады ой! ;)
if "%j2do%" == "nExit" (
  echo [!] %jInf%
  set /p tmp0=ANY key == EXIT
  set errorLevel=0
  echo %jDat% %time:~0,8% %~nx0 Ends /%dnLd%:dnLd %miss%:miss jYes=%jYes%/
  goto :eof
)

:reqOK что можно и следует делать, опроделились, надо запросить "добро" на...
echo [!] HAVE to %jInf% files
set tmp0=
echo ?ok to goOn [yes] == y
set /p tmp0=?[no]?
if /i NOT %tmp0%. == y. (
  echo job canceled
  exit
)

rem таки нада работать... wrks - общее время работы процесса /после "y" в ответ на запрос/
for /F %%A in ('gawk.exe "BEGIN{print(systime());exit}"') do set wrks=%%A
goto %j2do%

:creMig построение с нуля fMig со компанией
call :reCreMig
if defined errInf goto nExit
goto ending

:updMig подготовка к выборочному по fMig скачиванию статистик
set m2do=2
gawk.exe -f %aChk%
if errorLevel 1 set errInf=err in gAwk with %n2do%:n2do & goto nExit
set r2do=%n2do%
goto reSume

:dlHist подготовка к скачиванию всех годных статистик по списку в fHit
if NOT exist %jDir%*.* mkDir %jDir%
if errorLevel 1 set errInf=fail mkDir %jDir% &  goto nExit
rem gawk.exe "{if(NR-%hitN%>0)exit;if($4)print(\"2\",$5)}" %fHit% >%f2do%
gawk.exe "{if(NR-%hitN%>0)exit;if($4)a[++n]=$5}END{asort(a);for(j=1;j<=n;j++){print(\"2\",a[j])}}" %fHit% >%f2do%
for /F %%A in ('gawk.exe "END{print NR}" %f2do%') do set nUpd=%%A
set nPas=0
set nDel=0
set r2do=%nUpd%
set mark=%mark% --:nPre --:sEnd %nDel%:nDel %nUpd%:nUpd %nPas%:nPas
echo %mark% >>%f2do%
echo %mark% > %fDon%
echo %mark% > %fPas%

:reSume (ре)старт скачивания, нужны: f2do и fDon скачивается разницы (есть в первом, но нет во втором)
set m2do=4
rem список ошибок должно освобождать от старья
if %rDon% == 0 echo. >nul 2>%bErr%
rem красивый оживляж уместен всегда!
set begT=%time:~0,8%
echo ~ curDLjob: %rDon%:nDon %r2do%:n2do %begT%
for /F %%A in ('gawk.exe "BEGIN{print(%r2do%%%100);exit}"') do set tmp0=%%A
set /a jMax=%r2do%/100
if NOT %tmp0% == 0 set jMax=%jMax%+
set jMax=%jMax%      .
set jMax=%jMax:~0,5%
rem запустим "таймер" качалки
for /F %%A in ('gawk.exe "BEGIN{print(systime());exit}"') do set ratW=%%A
for /F "tokens=*" %%A in ('gAwk.exe -f %aChk%') do (
  if errorLevel 1 set errInf=gawk err: %%A & goto nExit
  call :reqSfil %%A
  if defined errInf goto nExit
)
rem остановим "таймер" качалки время работы в секундах
for /F %%A in ('gawk.exe "BEGIN{print(systime()-%ratW%);exit}"') do set ratW=%%A
rem среднее время скачивания файла в секундах
if %dnLd% == 0 (set ratW=0) else (
  for /F %%A in ('gawk.exe "BEGIN{print(sprintf(\"%%3.2f\",%ratW%/%dnLd%));exit}"') do set ratW=%%A
)
echo.
echo. == %cNum% %begT%-%time:~0,8% %ratW%~ratW
rem если дошли досюда - скачивание ОК
call :reCreMig
if defined errInf goto nExit
goto ending

:ending  выцепим главные итоги разбора статистик
rem ;2B stat 2021.04.26:sEnd  1457:nPre  20102:hPre  1457:miss 300:minT 548:hitL 30075:nHit last~01:01:31~
for /F %%A in ('gawk.exe "{if(match($0,/ ([0-9]+):miss /  ,a))print(a[1])}" %fMig%') do set miss=%%A
rem ;2C scan 2021.04.24:dEnd 19719:nEnd  60688:hEnd 30012:nMig 1657:tNum 11:nGis 29154:nLow mod:! OkV:65.7%
for /F %%A in ('gawk.exe "{if(match($0,/ ([0-9]+):tNum /  ,a))print(a[1])}" %fMig%') do set tNum=  %%A
for /F %%A in ('gawk.exe "{if(match($0,/ ([\.0-9]+):dEnd /,a))print(a[1])}" %fMig%') do set dEnd=%%A
for /F %%A in ('gawk.exe "{if(match($0,/ mod:(.) /        ,a))print(a[1])}" %fMig%') do set  mod=%%A
for /F %%A in ('gawk.exe "{if(match($0,/ OkV:([0-9\.]+)%%/,a))print(a[1])}" %fMig%') do set  OkV=%%A
set tNum=%tNum:~-4%
rem если грандов обнесли - готовый список использовать НЕЛЬЗЯ!
if "%mod%" == "!" (
  set jYes==NO
  set mark=~
) else (
  set jYes==OK
  set mark==
)
rem преобразуем секунды в время работы {в обычном формате "чч:мм:сс"}
for /F %%A in ('gawk.exe "BEGIN{print(strftime(\"%%H:%%M:%%S\",systime()-%wrks%+46800));exit}"') do set wrks=%%A
rem оформим подвал списка... соственно сборка закончена; jLst - итоговый список топовых текстов
gawk.exe "BEGIN{printf(\";2D %%s \",strftime(\"%mark%%%a%mark%\",systime()));exit}" >>%jLst%
echo.^
%tNum%:tNum ^
OkV:%OkV%%% ^
mod:%mod% ^
%dEnd%:dEnd ^
%sNul%:sNul ^
%jDat%:done ^
%time:~0,5% ^
%wrks%~wrks ^
%ratW%~ratW ^
%dnLd%:dnLd >> %jLst%
echo = itog ^
%tNum%:tNum ^
%sNul%:sNul ^
%ratW%~ratW ^
%jDat%:done ^
%wrks%~wrks ^
%time:~0,5%
rem и в общий отчёт ентот итог занесть нада-с
gawk.exe "{if(match($1,/^;2D$/))print($0)}" %jLst%  >> %jRpt%
rem и в fMig строку ;2D тож
gawk.exe "{if(match($1,/^;2D$/))print($0)}" %jLst%  >> %fMig%
rem уложим свежак в склад на полочку-с...
for %%A in (%wDir%*.%nJob%) do (
  copy /b /y %%A %zDir% > nul
  if errorLevel 1 set errInf=fail copy %%A to %zDir% & goto nExit
)
rem уложим туда же замеченные "дыры", ЕСЛИ сборка ВОЗМОЖНА
if "%mod%" == "v" (
  copy /b /y %wDir%.upDatGis.bStat.# %zDir%%dEnd%.%mod%.gis.%nJob% > nul
  if errorLevel 1 set errInf=fail copy %%A to %zDir% & goto nExit
)
rem подудим где нада "готово-с!"
set itog=%fMig%

:nExit если было проблем-с - известим всяко (особливо при запуске "с выключение компа")
if defined errInf echo !?. %errInf% & goto :eof
rem как оказалось, нулевые файлы таки заводятся иногда, проверим и прибьём найденное
for /F %%A in (
  'dir /a:-d /-c /o:s %fSta% ^| gawk "{if(NR>5)if($3>0){print NR-6;exit}}"'
) do set nulF=%%A
if NOT %nulF% == 0 (
  echo finds: %nulF% zero file{s} *.stat.htm, erasing...
  for /F %%A in (
    'dir /a:-d /-c /o:s %fSta% ^|gawk "{if(NR>5){if($3==0){print $4}else{exit}}}"'
  ) do (
    erase %jDir%%%A
    if errorLevel 1 set errInf=fail erase %%A & goto :eof
  )
)
rem  <li><i>Статистика рассчитывается раз в сутки. (Thu May 18 02:05:32 2023)</i>
:: смотрим дату обновления свежескачанной статистики nvv
for /F "tokens=1-9,*" %%A in (
'gawk.exe "{if(match($0,/<li><i>.+\. \(... (...) (..) ..:..:.. (20..)\)<\/i>/,a)){print(a[3],a[1],a[2]);exit}}" %nFil%'
) do (
  set tmp0=%%A.%%B.%%C
  set tmpA=%%A
  set tmpB=%%B
  set tmpC=%%C
)
:: НЕ изменилась в сравнении с уже имещщейся - значит и суетиться касаемо не надо
if %nDat%. == %tmp0%. goto nvvOk

:: ! иная дата, перекодируем месяц, построив дату в стандартном формате "гггг.мм.дд"
if %tmpB% == Jan set tmpB=01
if %tmpB% == Feb set tmpB=02
if %tmpB% == Mar set tmpB=03
if %tmpB% == Apr set tmpB=04
if %tmpB% == May set tmpB=05
if %tmpB% == Jun set tmpB=06
if %tmpB% == Jul set tmpB=07
if %tmpB% == Aug set tmpB=08
if %tmpB% == Sep set tmpB=09
if %tmpB% == Oct set tmpB=10
if %tmpB% == Nov set tmpB=11
if %tmpB% == Dec set tmpB=12
:: +добавляем /при нужде/ лидирующий ноль в день месяца
if %tmpC:~1,1%. == . set tmpC=0%TmpC%
:: и прикапываем...
echo nDat: %nDat% - %tmp0%
rem echo copy /b %nFil% %nDir%%tmpA%.%tmpB%.%tmpC%.%nSuf%
copy /b %nFil% %nDir%%tmpA%.%tmpB%.%tmpC%.%nSuf% >nul
rem echo run: D:\#ProgFile\Viewer\Viewer.exe %nDir%%tmpA%.%tmpB%.%tmpC%.%nSuf%
echo ! new NVV.stat, use U:\zNvvStat.cmd
:nvvOk
rem set /a wGetTot=%wGetTot:~0,-3%.%wGetTot:~-3% + 500
echo %jDat% %time:~0,8% %~nx0 Ends (%dnLd%:dnLd %miss%:miss jYes=%jYes%)
goto :eof

:chkNow проверка наличия f2do, если есть - сканирование шапки /реперы списков 1A и 2D/
rem ;9N 13:23~ 571717:hMax 2022.02.06:hitD --:nPre --:sEnd 0:nDel 493:nUpd 0:nPas 
rem ;9N 09:55~ 578491:hMax 2022.02.22:hitD 76:nPre :sEnd 0:nDel 20641:nUpd 9306:nPas 
rem     1      2           3               4
if exist %f2do% (
  for /F "tokens=1,*" %%J in (
    'gawk.exe "{if(match($0,/^;9N ([^ ]+)~ ([0-9]+):hMax ([^ ]+):hitD ([^ ]+):nPre ([^ ]*):sEnd/,a)){print(a[4],$0);exit}}" %f2do%'
  ) do (
    set s2do=%%J
    set o2do=%%K
  )
)
rem "--" - качаем ВСЁ по fHit, иначе - ЧТО-ТО по fMig
set m2do=3
if defined s2do (
  if "%s2do%" == "--" (
    set s2do=Hit
  ) else (
    set s2do=Mig
  )
rem реальное состояние текущего скачивания + upd? для справки
  for /F "tokens=1-9,*" %%J in ('gAwk.exe -f %aChk%') do (
    set rDon=%%J
    set r2do=%%K
  )
)
goto :eof

:reqSfil 2do id_P - обслуживание раздела согласно операции 2do /+оживляж +пополнение fDon/
set /a cNum+=1
set id_P=%2
set wFil=%jDir%%id_P:~0,1%~%id_P:~2%.stat.htm
set wUrl=%siUrl%/%id_P%/stat.shtml
rem 1 - pas - "-" - пропустить /даже если нету-с/
set tmp0=-
if %1 GTR 1 (
rem 3 - del - "X" - удалить /если есть/
  set tmp0=-
  if "%cNum:~-1%" == "0" set tmp0=%cNum:~-2,1%
  if exist %wFil% erase /F/Q %wFil%
  if errorLevel 1 set errInf=fail erase %wFil% & goto :eof
  if %1 == 2 (
rem 2 - upd - "*" - скачать/обновить {удалить+скачать}
  set tmp0=.
  if "%cNum:~-1%" == "0" set tmp0=%cNum:~-2,1%
  call wGetUrl2fil.cmd %wUrl% %wFil%
    if defined errInf goto :eof
rem     set /a wGetTot+=%wGetSiz%
    if %wGetSiz% == 0 (
      set /a sNul+=1
      echo x sNul %wUrl% >> %bErr%
      erase /F/Q %wFil%
      if errorLevel 1 set errInf=fail erase %wFil% & goto :eof
    )
    if errorLevel 1 set errInf=fail erase %wFil% & goto :eof
  )
)
echo %* >> %fDon%
gawk.exe "BEGIN{printf(\"%tmp0%\");exit}"
set tmp0=    %cNum%
set tmp0=%tmp0:~-6,4%.%jMax%
rem if "%cNum:~-2%" == "00" echo. %tmp0:~-6,4%.%jMax%%time:~0,8%
if "%cNum:~-2%" == "00" (
  gawk.exe "BEGIN{printf(\" %%s\",\"%tmp0%\");exit}"
  echo.
)
goto :eof

:reCreMig разбор содержимого файлов статистики, сначала убираем точно устаревшее когда-то ранее
for %%A in (%wDir%*.old) do (
  erase /F/Q %%A
  if errorLevel 1 set errInf=fail erase %%A & goto :eof
)
rem двигаем в .old то, что устареет сейчас
for %%A in (%wDir%*.%nJob% %wDir%*.2do) do (
  move /y %%A %%A.old
  if errorLevel 1 set errInf=fail move %%A 2old & goto :eof
)
rem закроем открытые строки уставок updBeg & updEnd {если есть}
if "%updB%%updE%%updN%" == ",,k." goto noUpdNow
gawk.exe "{if(match($1,/^x/)){print($0)}else{print(\"x\",$0)}}" %fUnt% > %fUnt%~
move /Y %fUnt%~ %fUnt%
if errorLevel 1 set errInf=fail move %fUnt%~ to *.*  & goto :eof
rem сохраним принятые уставки {там же в %fUnt%} для истории
if "%updB%" == "," set updB=----.--.--,
if "%updE%" == "," set updE=----.--.--,
if "%updN%" == "k." set updN=--k.
echo x Start: %jDat% %time:~0,5% %updB%updB %updE%updE %updN%updN >>  %fUnt%
:noUpdNow собственно построение с нуля jLst и fMig
gawk.exe -f %aScn% %jDir%*.stat.htm
if errorLevel 1 set errInf=fail scaning *.stat.htm
rem затем выявляем свежак jLst - итоговый список годных "начерно" текстов, набравших minT хитов за 30 дней
set jLst=
for %%J in (%wDir%20*.lst.%nJob%) do set jLst=%%J
if NOT defined jLst set errInf=fail mk 20*.lst.%nJob% & goto :eof
rem и свежак fMig - итоговый список годных "начерно" текстов, набравших minT хитов за 30 дней
set fMig=
for %%J in (%wDir%20*.mig.%nJob%) do set fMig=%%J
if NOT defined fMig set errInf=fail make 20*.mig.%nJob%
goto :eof

:chkDel2do %%A НЕ сегодняшние 2do точно НЕ нужны!
set tmp0=%~n1
if NOT %tmp0:~0,10% == %jDat% erase /F/Q %1
if errorLevel 1 set errInf=fail erase %1
goto :eof

rem 2019.07.15 creation from siTop-dStat.cmd {2019.07.11}
rem ...
rem 2020.01.06, nvv, {!}ЕСЛИ гранд СИ попал в отсеянные по ДАТЕ обновления - сборка рейтинга НЕВОЗМОЖНА!
rem 2020.03.27, nvv, радикально переделано {ВСЕ проверки и ВСЕ итоги - в bStat1.awk}
rem   + более тонкая работа с наличностью {с уже имеющимися файлами статистик}
rem 2020.04.25, nvv, some updates
rem 2020.05.31, nvv, нижняя граница для разделов из списка *.aHits - hitP>hitL {==670}, а не конец списка
rem 2020.06.05, nvv, hitL=665
rem         08            650
rem         10            645
rem         14            610
rem 2020.06.18, nvv, нижнюю границу {hitL=NNN} можно задать файлом-маркой "NNN.lowHits"
rem 2020.08.21, nvv, статистики, обновлённые "СЕГОДНЯ" или с той же датой ВЕРХНЕЙ границы интервала
rem   НЕ удаляются! Т.е. киллер не трогает сегодняшний комплект для рассчёта
rem 2020.09.18, nvv, слегка перепахано для более тонкого разбора наличности
rem   + итоговый jLst формируется сразу отсортированный и оформленный
rem     + в конце строк с атрибутами отобранных текстов - datJ {1-й_полный_день}
rem 2020.09.30, nvv, исправлена неточность в работе со списком BadStats {если упомянут в ]bStat.rpt}
rem 2020.10.12, nvv, мелкая косметика
rem 2020.11.02, nvv, исправлена ошибка обработки списка LowStats
rem 2020.11.13, nvv, будКлуб - глупость
rem 2020.11.16, nvv, будКлуб - глупость? не факт... ;{
rem 2020.12.18, nvv, добавлена возможность принудительно задавать дату верхней границы интервала
rem     в 30 дней для обсчёта {занадобилось после аварий 2020.11.19 - "ледяной дождь" + паралич в СИ}
rem 2020.12.28, nvv, \siUrl.budclub turn to budclub.ru
rem 2021.01.29, nvv, если в корне нет NNN.lowHits, hitL == хиты "за 12мес" 30000-го раздела в *.aHits
rem 2021.03.23, nvv, gawk для вычленения первых 30000+ записей aHits
rem 2021.03.25, nvv, в итогах явно указывается % годных к расчёту статистик
rem 2021.05.17, nvv, очередная оптимизация с унификацией...
rem 2021.05.27, nvv, границы "от-доНЕвключая" запоминаются ДО обновления и юзаются ПОСЛЕ падения {если}
rem 2021.06.19, nvv, иной формат {подчерк "_" вместо пробела " " в границах updB & updE}
rem 2021.07.30, nvv, мелкая косметика
rem 2021.11.16, nvv, мелкая косметика касаемо tNum {пробелы слева до 4-х знаков}
rem 2022.01.19, nvv, начало оптимизации при рестарте {за день; ресканирование ВСЕХ статистик избыточно}
rem 2022.02.06, nvv, радикально переделано, теперь готово работать сразу /без дум глубоких пред/
rem 2022.02.16, nvv, таки заработало(!) вменяемо сие радикально-с переделанное
rem 2022.02.23, nvv, исправлена пара неточностей
rem 2022.03.04, nvv, нулевые файлы статистик удаляются /если найдутся/ перед завершением работы
rem 2022.03.23, nvv, теперь список статистик из aHIts /при скачивании ВСЕХ/ сортируется по алфавиту
rem 2022.04.04, nvv, теперь таймеры показывают не текущее время, а прошедшее с запуска
rem 2022.07.11, nvv, если делать нечего, то и нефик искать нулевые файлы статистики
rem 2022.08.10, nvv, +updNum==updN - возможнось явно задать в .upDatSet.bStat.# кол-во обновляемых статистик
rem 2022.09.03, nvv, minor improvements
rem 2022.09.20, nvv, обихожено взаимодействие с bStat0.awk
rem 2022.09.29, nvv, исправлены ошибки считывания r2do при перезапуске упавшего процесса
rem 2022.10.15, nvv, minor improve
rem 2023.01.03, nvv, сменил разрешение экрана /меньше пикселей/, пришлось менять оживляж
rem   + добавлено прикапывание файла статистики nvv
rem 2023.01.15, nvv, minor cosmetics
rem 2023.03.09, nvv, was: set minT=300, now: 200
rem 2023.03.23, nvv, добавлено прикапывание "дыр" *.v.gis.bStat в статистике в Stor
rem 2023.04.16, nvv, сущая мелочь не нужная удалена
rem 2023.05.06, nvv, папка nDir перемещена в Stor и лишилась префикса '#'
rem 2023.05.21, nvv, исправлена ошибка в имени целефого файла в папке nDir + "DLобъём"
rem 2023.06.28, nvv, +поддержка: add2 - список разделов-потеряшек для добавления в расчёт
rem 2023.07.05, nvv, удалена поддержка: add2 потерянное вносится в список aHits

    Имя файла скрипта: "bStat0.awk" V-текст скрипта-V
- - - - - - - - -
BEGIN{            #  bStat0.awk анализатор текущего расклада статистик по спискам и по наличности
#   test=1            #  режим отладки - формируется выборка из *.mig.bStat удаляемого/обновляемого
  vMin=5000       #  осталось не больше в "чёрный" понедельник? == рестарт цикла вместо завершения
  vFat=10000              #  самый "жирный" день должен иметь не меньше чем треть статистик в нём
  vMax=15000                                      #  обновлять сегодна "не больше"

#  источники при составлении списка удалить/обновить/скачать
#  1. aHits - ОБЯЗАТЕЛЕН (без - бессмысленно)
#  2. \Jobs\.delUntil.bStat.# - принудительные уставки по датам (необязательно)
#  3. \Jobs\*.mig.bStat - справка о раскладе по датам статистик (если есть, то непусто!)
#  4. \Jobs\*.2do - списки .2do. .don. и .pas. СЕГОДНЯ (могут отсутствовать)
# [!] envVar m2do задаёт режим работы (1..4); нечётные: "справка", чётные: "комплект файлов 2do"

# 2023.07.15, nvv, исправлена неточность в обработке списка aHits
# ! прочая история в подвале

  if(test){
    m2do=1          #  job: 1-сводка"upgMig" 2-построчно.file 3-сводка"2doDif" 4-построчно.StdOut
    jDat=strftime("%Y.%m.%d")                     #  "сегодня"
#       jDat="2022.09.17"
    wDay=7                #  The weekday as a decimal number (1-7). Monday is day one. in strftime
    topN=30000                                    #  нужное макс+ число топовых разделов
    jDir=".\\"
    wDir="U:\\Jobs\\"
#     wDir=jDir
    fHit=wDir "2023.06.29.aHits"
#       fHit=jDir"2022.09.06.aHits"
    fMig=wDir "2023.06.28.~.mig.bStat"            #  160 85 1 20
    f2do=jDir "test.upd.2do.2do"                  #  то, что НУЖНО "удалить/обновить"
    fDon=jDir "test.upd.don.2do"                  #  то, что УЖЕ "удалено/обновлено"
    fPas=jDir "test.upd.pas.2do"                  #  то, что пропущено (для полноты картины-с)
    fUnt=wDir ".upDatSet.bStat.#"             #  принудительные уставки по датам (может быть пуст)
  }else{
    m2do=getEnv("m2do")   #  1-сводка"upgMig" 2-построчно.file 3-сводка"2doDif" 4-построчно.StdOut
    jDat=getEnv("jDat")                           #  "сегодня"
    if(!jDat)jDat=strftime("%Y.%m.%d")
    wDay=strftime("%u",mktime(substr(jDat,1,4)" "substr(jDat,6,2)" "substr(jDat,9,2)" 12 00 00"))
    topN=getEnv("topN")                           #  минимальное нужное число топовых разделов
    wDir=getEnv("wDir")                           #  место общих списков
    fHit=getEnv("fHit")                           #  ВХОДНОЙ список *.aHits
    fMig=getEnv("fMig")                   #  отсортированный список моментов обновления статистик
    f2do=getEnv("f2do")                           #  список нужного к скачиванию
    fDon=getEnv("fDon")                           #  список уже оптиченного сегодня
    fPas=getEnv("fPas")                           #  список пропускаемого сегодня
    fUnt=getEnv("fUnt")                       #  принудительные уставки по датам (может быть пуст)
  }
  if(wDir==""){
    errS="] NOT 'testMod' NOR 'cmdCall'"
    exit
  }
# загрузка приНУдительных /могут быть пусты/ границ дат обновления статистик (до/вне НЕ обновлять)
# x updBeg~2021.05.09~ x GEQ (same or after) date[ time]
# x updEnd~2021.05.12~ x LSS (until) date[ timt] (~yyyy.mm.dd[ HH:MM:SS]~)
  while((getline jj<fUnt)>0){                     #  ищем уставки ГРАНИЦ дат datS статистик
    if(match(jj,/^ *updBeg~([^~]+)~ /,aj))updB=aj[1]  #  updBeg~2021.04.18_00:18:07~ "ОТ"
    if(match(jj,/^ *updEnd~([^~]+)~ /,aj))updE=aj[1]  #  updEnd~2021.04.19~ "до НЕ включая"
    if(match(jj,/^ *updNum~([^~]+)~ /,aj))updN=aj[1]  #  updNum~10~ "с начала и до этой стоки"
  }
  close(fUnt)
#  секция "ПРОДОЛЖИМ": читаем "сделать"(стабл)+"готово"(растёт) => на StdOut "сделать" (построчно)
  nDon=0              #  загрузка списка fDon "уже сделано" (может быть пуст) - защита от падений
  while((getline jj<fDon)>0){
    if(match(jj,/^([23]) ([^ ]+)/,aj)){           #  2 m/moskalenko
      nDon++                                      #  3 s/s_w
      aDon[aj[2]]=aj[1]                           #  статус "КАК" /раздел/ был "оптИчен"
    }
  }
  close(fDon)
  n2do=0                                #  загрузка списка f2do - то, что НУЖНО "удалить/обновить"
  while((getline jj<f2do)>0){
    if(match(jj,/^([23]) ([^ ]+)/,aj)){           #  2 m/moskalenko
      if(!(aj[2] in aDon)){                       #  3 s/s_w
        n2do++
        a2do[n2do]=aj[1]                          #  статус "КАК" /раздел/ "оптИчить"
        aIdP[n2do]=aj[2]                          #  ID этого раздела
      }
    }
  }
  close(f2do)

  if(m2do==3){                                    #  собственно выдача итога "разница" СВОДКОЙ
    print(nDon,n2do)
    exit
  }
  if(m2do==4){                              #  собственно выдача итога "разница" СПИСКОМ на StdOut
    for(jj=1;jj<=n2do;jj++)print(a2do[jj],aIdP[jj]) #  2do id_P
    exit
  }

# секция "НАЧНЁМ": читаем aHits+"уставки"("хочу") => 3 файла: "сделать"+"готово"(пуст)+"пропустить"
  jk=0          #  загрузка списка aHits. первичное заполнение массива id_P (дОлжных быть раздлов)
  hitL=0                #  минимальные хиты раздела за 12мес. (реальный минимум - по topN в aHits)
  nHit=0
  while((getline jj<fHit)>0){                     #  чтение aHits
#  362225      6    1133      3 k/kadawr "Кадавр" "no•comments"
# ; hitP.1 numP.2 sizK.3 4.sizN 5.id_P 6."namP" 7."moto"
    split(jj,aj)
    if(match(aj[1],/^[0-9]+$/)){
      if(hitL-aj[1]<=0){                          #  перешли искомую границу чтения списка aHits
        jk++
        if(aj[4]){
          aHit[aj[5]]=1                           #  пустые разделы нам не интересны...
          nHit++                            #  число реально годных разделов с достаточными хитами
        }
        if(jk==topN)hitL=aj[1]            #  запомним хиты границы для отсечения меньших значений
      }
    }else{
# ;1A hits 4:xNam 449:xNum 102533:xPas 2022.02.03:hDat 571717:hMax 1030:hPag 1031:dnLd 0.32~ratW 2022.02.06:done 00:22:49~wrks 15:10
      if(match(jj,/;1A .+ ([^ ]+):hMax .+ ([^ ]+):done/,aj)){
        hMax=aj[1]
        hitD=aj[2]
      }
    }
  }
  close(fHit)
  if(nHit-100<0){
    errS="] NO or FEW recs:"nHit" in "fHit        #  мало
    exit
  }
#  загрузка списка fMig. окончательное формирование массива статусов id_P
  full=0
  stop=""
  if(fMig!=""){   #  вообще-то этот файл может отсутствовать, но если есть, то не может быть пуст
    while((getline jj<fMig)>0){                   #  чтение mig.bStat
      split(jj,aj)
      if(match(jj,/^;/)){     #  различие записей в секциях: "справка" и "данные" (даты статистик)
# ;2B stat 2022.02.10:sEnd 0:nPre 0:hPre 0:miss 300:minT 4530:hitL 500:nHit last~01:11:36~
        if(match(jj,/^;1A .+ ([^ ]+):hMax .+ ([^ ]+):done/,ak))migH=ak[1]":"ak[2]
        if(match(jj,/^;2B .+ ([^ ]+):sEnd +([^ ]+):nPre /,ak)){
          sEnd=ak[1]                              #  для контрольки в списках 2do
          nPre=ak[2]                              #  для контрольки в списках 2do
        }                                         #  v- aHits IN *.mig.bStat
#         if(match(jj,/^;2D .+ mod:(.) /,ak))full=(ak[1]=="v")
# ;2D =Сб= 896:tNum OkV:98.7% mod:v 2023.01.11:dEnd 0:sNul 2023.01.14:done 15:26 00:24:23~wrks 1.54~ratW 52:dnLd
        if(match(jj,/^;2D .+ mod:(.) ([^ ]+):dEnd /,ak)){
          full=(ak[1]=="v")
          dEnd=ak[2]
        }
# ;     datS_timS.1     datJ.2 numP.3 hitP.4 id_P.5 id_P.6 "namP".7
# ; upDat_datS  1-st_day   numS xDel/=Keep hitP  numP   hitT id_P id_T
# ; 2021.05.08-2021.05.07     2  0.0% x       ~     ~      ~ ~    ~
# ; 2021.05.12-2021.05.11    10  0.0% x       ~     ~      ~ ~    ~
# ; 2021.05.13-2021.05.11     6  0.0% x       ~     ~      ~ ~    ~
# ; 2021.05.14-2021.05.11   609  2.0% x  199572    14  23447 z/zhan flibustain
# ; 2021.05.15-2021.05.14 20734 69.2% =  734831     1  62477 m/metelxskij_n_a wtt-2
# ; 2021.05.16-2021.05.14  8621 28.8% =  342690     6  36311 s/sedrik loki
# 1           2               3     4 5       6     7      8 9
        if(match(aj[5],/^([=x])$/)){
          nDat++
          aDtD[nDat]=aj[2]                        #  массив: datS-datJ
          split(aj[2],ak,"-")
          last=ak[1]                              #  макс datS дата обновления статистик
          aDtJ[nDat]=ak[2]                        #  массив: datS-datJ
          if(aj[6]=="~")aDtV[nDat]=aj[6]          #  признак: "учитывать" == число в hitP
          else{
            aDtV[nDat]="+"                        #  учитываем...
            nV++
            aV0[nV]=nDat                          #  место в общем массиве
            aV3[nV]=aj[3]       #  кол-во статистик за этот "важный" день /массив "важняк": numS/
          }
          if(rMax-aj[3]<0){                       #  ищем самый "толстый" день
            nMax=nDat                             #  запоминаем место этого дня в общем массиве
            rMax=aj[3]                            #  и кол-во статистик в нём
          }
        }
      }else{                                      #  это /основная/ запись дат статистики раздела
# 0000.00.00_00:00:00 _bad_file_    0    0 w~wladimir_kasxjanow not_IN_aHits w/wladimir_kasxjanow
# 0000.00.00_00:00:00 _bad_file_  891 3967 g~goncharow_d_a "No.Wen" g/goncharow_d_a
# 2021.04.17_15:41:28 2021.04.16 5758 1129 a~aam a/aam "А.А.М."
# 2022.09.14_00:39:10 2022.09.11   129   11429 s~sks s/sks "Скс"
# 2022.09.17_00:52:48 2022.09.15    92   15152 n~nosow_w_w n/nosow_w_w "Влад"
#  1                  2          3    4    5     6      7
        aMig[aj[6]]=aj[1]   #  "дата_время" обновления статистики этого id_P (2022.09.14_00:39:10)
        aNum[++nNum]=aj[6]                        #  место id_P в *.mig.*
        aDat[nNum]=substr(aj[1],1,10)"-"aj[2]   #  datS-datJ в этом месте (2022.09.17-2022.09.15)
      }
    }
    close(fMig)
    if(rMax-vFat<0){                #  самый "жирный" день должен иметь больше трети общего объёма
      errS="] too low rMax:"rMax" in "fMig
      exit
    }
#  разбраковка fMig
    if(updN updB updE == ""){   #  "автоматическая" разбраковка fMig (по справке в "*,mig.bStat")
      if((last==jDat)&&(hMax":"hitD==migH)){ # если сегодня уже было И aHits тот же (НЕ изменился)
        errS="] already WAS today's {"last"} UPdates, to REPEAT use: "fUnt  #  идём за уставками!
        exit
      }
#           last=ak[1]                              #  макс datS дата обновления статистик
      if(last==jDat){                             #  сегодня уже ВСЁ отрабатывало есть новый aHits
        for(id_P in aMig)aMig[id_P]=1             #  == pas уже имеющееся обновлять НЕ нужно
      }else{
        if(full)stop=aDtD[nMax+1]       #  1-й день нового расчёт, upDat самой "богатой" пары дат
        else{                                     #  иной /не 1-й/ день расчёта
          rv=0                                    #  выясняем сколько "важняка" следует обновить
          for(jj=1;jj<=nV;jj++){
            rv=rv+aV3[jj]                         #  <-v- суммируем пока НЕ обновлённый "важняк"
            if((aDtJ[aV0[jj]]==dEnd)||(rv-vMax>0))break #  но не больше приемлемого
          }
          if((wDay==1)&&(rv-aV3[jj]<vMin)){       #  если понедельник + окончание цикла
            stop=aDtD[nMax+1]                   #  !!! будет новае значение dEnd /сдвоенный цикл/
          }else stop=aDtD[aV0[jj]]            #  иначе - стоп где остановилось сложение "авжняков"
        }
        for(jj=nNum;jj>0;jj--){               #  "не сегодня" + "любой aHits" разбраковка по stop
          aMig[aNum[jj]]=(aDat[jj]<stop)+1 #  меньше=="обновить", иначе=="пропустить" 1/2==pas/upd
        }
      }
    }else{            #  иначе, "ручная" разбраковка fMig (по факту уставок в ".upDatSet.bStat.#")
      if(updN==""){nJob="99999"}else{nJob=updN*1000}  #  кол-во обновляемого: "всё"
      if(updB==""){bJob="2000."}else{bJob=updB}   #  начиная с момента: "с САМОГО начала"
      if(updE==""){eJob="3000."}else{eJob=updE}   #  до не включая момента: "ЗА концом списка"
      for(jj=1;jj<=nNum;jj++){                    #  для всех записей в fMig
        id_P=aNum[jj]         #  с момента bJob по /не включая/ момент eJob, но небольше чем nJob
        if((bJob<=aMig[id_P])&&(aMig[id_P]<eJob)&&(nJob>0)){
          nJob--
          j2do=2                                  #  == upd
        }else{
          j2do=1                                  #  == pas
        }
        aMig[id_P]=j2do
      }
    }
  }
  split("",a2do)                                  #  заполнение полного массива действий
  for(id_P in aMig){                              #  проекция aMig на aHit
    if(id_P in aHit){a2do[id_P]=aMig[id_P]}       #  есть в aHit - берём 2do из aMig
    else            {a2do[id_P]=3         }       #  нет в fHit - удалить!
  }
  for(id_P in aHit){                              #  проекция fHit на fMig
    if(!(id_P in aMig))a2do[id_P]=2               #  нет - скачивать!
  }
  nTot=0
  for(id_P in a2do)aTot[++nTot]=id_P" "a2do[id_P] #  строим полный список для сортировки...
  asort(aTot)
  nPas=0                                          #  счётчик "оставить" (для отчётности)
  nUpd=0                                          #  счётчик "обновить"
  nDel=0                                          #  счётчик "удалить"
  split("",a2do)                                  #  заполнение полного массива действий
  for(jj=1;jj<=nTot;jj++){
    split(aTot[jj],aj)
    if(aj[2]==1)aPas[++nPas]=aj[1]                #  1 pas "оставить"
    if(aj[2]==2)aUpd[++nUpd]=aj[1]                #  2 upd "обновить"
    if(aj[2]==3)aDel[++nDel]=aj[1]                #  3 del "удалить"
  }
  if(m2do==1){                                    #  справка по запросу для оценки объёма работ
    if(test)print("nPas,nDel,nUpd,nTot,updB,updE,updN.")
    print(nPas,nDel,nUpd,nTot,updB",",updE",",updN"k.")
    exit
  }
  if(m2do==2){                                #  списки по запросу для последующего использования
# ;9N 21:04~ 571717:hMax 2022.02.06:hitD --:nPre --:sEnd 0:nDel 493:nUpd 0:nPas 
    jk=";9N "strftime("%H:%S")"~ "hMax":hMax "hitD":hitD "nPre":nPre "sEnd":sEnd "\
nDel":nDel "nUpd":nUpd "nPas":nPas "
    for(jj=1;jj<=nPas;jj++)print(1,aPas[jj]" ") >fPas #  пропускаемое - в отдельный список
    for(jj=1;jj<=nDel;jj++)print(3,aDel[jj]" ") >f2do #  обрабатываемое - в другой, на удаление
    for(jj=1;jj<=nUpd;jj++)print(2,aUpd[jj]" ") >f2do #  затем (туда же) - на обновление
    print(jk)>fDon                                #  текущая метка для порядка
    print(jk)>fPas                                #  во все
    print(jk)>f2do                                #  три созданных списка
    exit
  }
  errS="] ado happens, m2do:"m2do
  exit
}

function getEnv(env1) {               #  найти в массиве ENVIRON значение переменной с именем env1
  for(env0 in ENVIRON){                     #  ! реГИстр букв в имени переменной МОЖЕТ быть лЮбОй
    if(tolower(env0)==tolower(env1)){             #  имя переменной совпадает с указанным?
      return(ENVIRON[env0])                       #  вернём значение этой переменной
    }
  }
  return("")                                      #  ничего нет? знать не судьба...
}

END{                                              #  отстрелялись; ну почти...
  if(errS!=""){
    print("")
    print(errS)                                   #  есть проблем-с => подудим
    exit -1                                       #  и на выход с вещами
  }
# 2021.04.20, nvv, created
# 2021.04.26, nvv, выходной список сортируется в порядке mig.bStat
# 2021.05.02, nvv, мелкая косметика
# 2021.05.29, nvv, полу-автоматический выбор границ удалить/обновить/скачать теперь здесь
# 2021.06.18, nvv, иной формат *.mig.bStat
# 2021.11.07, nvv, мелкая косметика
# 2022.02.14, nvv, радикально переделано и наконец-то заработало как надо
# 2022.02.23, nvv, исправлена пара неточностей
# 2022.03.12, nvv, уточнена интерпретация явно заданых границ обновления
# 2022.08.11, nvv, +updNum==updN - путь задать в .upDatSet.bStat.# кол-во обновляемых статистик
# 2022.08.22, nvv, уточнена работа с явными границами в .upDatSet.bStat.#
# а. updBeg~2022.08.20~ == с этого момента в дате обновления статистики НУЖНО скачать свежую копию
#     ! может быть пусто ~~ == "с начала списка"
#   б. updEnd~2022.08.20_01:18~  == с этого момента НЕ обновлять
#     ! может быть пусто ~~ == "до конца списка"
#   в. updNum~10~  == кол-во скачиваемых ТЫСЯЧ статистик с момента updBeg
#     ! или сначала, если updBeg не задано
#     + updEnd ИГНОРИРУЕТСЯ!
#   !!! параметр (updBeg/updEnd/updNum) должен быть терминирован пробелом и стоять первым в строке
#     + иначе он игнорируется (считается пустым)
#     + используется ПОСЛЕДНЕЕ непустое вхождение параметра, если таковых не одно
#     + если пусты все три == явных (заданных вручную) границ нет
# 2022.09.22, nvv, исправлена работа с "ручными" границами в .upDatSet.bStat.# /более вменяемая/
# 2022.09.29, nvv, откат ненужного улучшения выдачи в m2do=3
# 2022.10.18, nvv, обустроен altStop - иное вычленение статистик "на upDat" /не по bStat.awk/
# 2023.01.14, nvv, altStop теперь не флаг, а число самых свежих НЕ обноляемых статистик
# 2023.01.18, nvv, altStop теперь не нужен, изменился рисунок обновления статистик
# 2023.04.20, nvv, более гибкий алгоритм поиска даты "до не включая" updEnd по вчерашнему раскладу
# 2023.05.21-2023.07.02, nvv, +поддержка: add2 - список разделов-потеряшек для добавления в расчёт
# 2023.07.05, nvv, удалена поддержка add2, потерянное вносится в список aHits
}

    Имя файла скрипта: "cChkH.awk" V-текст скрипта-V
- - - - - - - - -
BEGIN{      # cChkH.awk - хиты текста по факту в интервале от "Размещен:" до верхней границы
  if(ARGV[1]=="")exit                             # защита от случайного пуска

# 2020.06.10, nvv, creation, на основе siTop-bStat.awk от 2020.05.31
# 2020.06.16, nvv, it's working! =)
# 2020.12.19, nvv, добавлена принудительная установка верхней даты (datJ) нашего интервала
# 2021.05.02, nvv, мелкая косметика

  for ( str2 in ENVIRON ) {                   # ! регистр букв в имени переменной может быть любой
    str1 = tolower( str2 )                        # Temp и temp - РАЗНЫЕ имена для gAwk...
    if ( str1 == "begd" ) begD=ENVIRON[str2]      # дата "Размещен:"
    if ( str1 == "datj" ) datJ=ENVIRON[str2]      #  верхняя граница интервала: datJ
    if ( str1 == "id_t" ) t_id=ENVIRON[str2]".shtml"  # имя файла с текстом, чьи хиты нужны
  }
#  begD="2020.11.23"
#  datJ="2020.12.08"
#  t_id="19.shtml"

# массив имён месяцев для преобразования их в числа с лидирующим нулём 01..12
  mNum["Jan"]="01"                                # .1 January . Jan. 31 . winter wint
  mNum["Feb"]="02"                                # .2 February .Feb. 28/29
  mNum["Mar"]="03"                                # .3 March . . Mar. 31 . spring sprn
  mNum["Apr"]="04"                                # .4 April . . Apr. 30
  mNum["May"]="05"                                # .5 May . . . May. 31
  mNum["Jun"]="06"                                # .6 June . . .Jun. 30 . summer summ
  mNum["Jul"]="07"                                # .7 July . . .Jul. 31
  mNum["Aug"]="08"                                # .8 August . .Aug. 31
  mNum["Sep"]="09"                                # .9 September Sep. 30 . autumn autm
  mNum["Oct"]="10"                                # 10 October . Oct. 31
  mNum["Nov"]="11"                                # 11 November .Nov. 30
  mNum["Dec"]="12"                                # 12 December .Dec. 31 . winter wint
  hitT=0                                          # сумма хитов "за 30 дней"
  step=0          # 0-владелец, 1-дата обновления, 2-id раздела, 3-разбор строк таблиц статистики
  inTab=0                                         # сначала - мы НЕ "в таблице"
  numT=0                                          # счётчик таблиц в файле статистики
  numR=0                                          # счётчик строк в текущей таблице
  selN=0                                          # номер в строке обрабатываемой ячейки
  selE=0                                      # номер ячейки с датой "Размещён:" (последней НЕ 0!)
# объявим массивыы для общего использования в подпрограммах
  split("",aSel)      # в ячейках статистики: url,title,hits:all,12mon,mon0..monC-11,day0..day0-61
}

{                                                 # // -=- основной блок скрипта begs
  if(step==0) {                                   # ищем/проверяем ФИО автора
# <h3>Статистика раздела &quot;<a href=./>Влад</a>&quot;:</h3>
    if (match($0,/<h3>Статистика раздела \&quot;<a href=\.\/>(.*)<\/a>\&quot;:<\/h3>/,aj)) {
      step++                                      # проверка ОК
    }
  }else if(step==1){                              # вычленеие даты обновления статистики
    if(match($0,\
/<li><i>Статистика рассчитывается .+\. \(.+ (...) +([0-9]+) (..:..:..) (20[0-9][0-9])\)<\/i>/,aj)){
      if(length(aj[2])==1) aj[2]="0"aj[2]         # причешем первые дни месяца "1" -> "01"
      ticS=mktime(aj[4]" "mNum[aj[1]]" "aj[2]" 12 00 00") # тики на 12 часов даты статистики
      datS=aj[4]"."mNum[aj[1]]"."aj[2]
      step++                                      # дату статистики оптичили
    }
  }else if(step==2){                              # ищем authID (!должен быть)
#[<a href=http://top.mail.ru/pages?id=77427&period=0&date=2018-10-02&filter_type=0
#&filter=n/nosow_w_w&pp=20&gender=0&agegroup=0>по дням</a>]
    if (match($0,/\[<a href=http.+&filter=([0-9a-z]\/[\-0-9a-z_]+)&pp=.+>по дням<\/a>]/,aj)) {
      step++                                      # проверка ОК
    }
  }else if(step==3){                # таблицы статистики, просмотр строк, сборка данных в ячейках
    if($0=="<!--------- Подножие ------------------------------->"){  # разбор закончен
      step++
      nextfile
    }
    if(inTab){                      # Мы ВНУТРИ таблицы, собираем строки таблицы с хитами текстов
      buf0=buf0" "$0                              # gain inpLines to buf0, then check contents
      while((match(buf0,/<tr ?[^>]*>/))||(int0=index(buf0,"</tr"))){ # seek <tr .. </tr for tabRow
        if(RSTART){                               # new/next line begs
          numR++
          buf0=substr(buf0,RSTART+RLENGTH)
        }
        if(int0=index(buf0,"</tr")){              # this line ends
          sel0=0                                  # хиты до накануне-v
          sel1=0                                  # хиты накануне даты "Размешен:"
          getSels(substr(buf0,1,int0-1))          # tabl_row -> arrow:sell_values
          buf0=substr(buf0,int0+4)                # убрали эту строку из буфера и окультуриваем...
          id_T=aSel[0]                        # имя+расшир файла с текстом (или "./" для раздела)
          if((numT==1)&&(numR==2)){               # хиты по разделу (спец.обслуживание)
          }else{                                  # обычная строка статистики текста
            if(id_T==t_id){
              if(!sel0)hitT=hitT+sel1     # если это глюк СИ (хиты накануне размещения), учтём их
              print(hitT)
              exit
            }
          }
        }
      }
      if(int0=index(buf0,"</table")){             # this tabl ends
        buf0=substr(buf0,int0+4)                  # update buf0
        inTab=0
      }
    }else{                                        # ищем начало таблицы
      if(int0=index($0\
,"<table border=1 cellspacing=0 cellpadding=0><tr><td><td colspan=2><b>Итого</b></td>")){
        numT++                                    # new table
        numR=1                  # певая строка каждой таблицы - календарь хитов статистики раздела
        buf0=substr($0,int0+80)
        if(numT==1){                              # 1-st table 1-st row -> get col-dates & so-on
          match(buf0,/<tr[^>]*>(.+)<\/tr[^>]*>/,aj) # собственно строка с календарём
          getSels(aj[1])                          # но самую первую такую - разберём-с
          tic0=ticS             # и привяжем дату к нулевому (самому левому) дню посуточных данных
          while(strftime("%d",tic0)!=aSel[16])tic0=tic0-86400 # сдвинем стат.дату к нулевому дню
          for(dev0=0;dev0<30;dev0++)if(strftime("%Y.%m.%d",tic0-86400*dev0)==datJ)break
          tic0=tic0-86400*(dev0-1)
          dev0=dev0+15
          for(selE=1;selE<31;selE++)if(begD>=strftime("%Y.%m.%d",tic0-86400*selE))break
        }                                         # ^- поиск номера яцейки с датой "Размещен:"
        inTab=1                                   # мы ВНУТРИ таблицы
        buf0=""                                   # буфер чист!
      }
    }
  }
}                                                 # // -=- основной блок скрипта ends

function getSels(rowLn){                # разделение строки таблицы статистики на массив значений
  hitT=0                                          # сумма хитов "за 30 дней"
  selN=0                                          # sells count
  split("",aSel)
  aSel[0]=""
  while(match(rowLn,/<td[^>]*>/)){                # это объявление ячейки
    selN++                  # 0.1-имя/ссылка; хиты: 2-всего, 3-12мес, 4..15-заМес, 16..77-за сутки
    rowLn=substr(rowLn,RSTART+RLENGTH)            # выкусим объявление ячейки
    f3n2=index(rowLn,"</td")                      # это конец ячейки
    f3s2=substr(rowLn,1,f3n2-1)               # это сама ячейка (с возможными внутренними тегами)
    if(selN==1){                              # в ПЕРВОЙ ячейке - название текста и ссылка на него
      match(f3s2,/<a href=([^>]+)>(.*)<\/a>/,f3a1)  # выцепим и разложим:
      aSel[1]=f3a1[2]                             # название текста
      aSel[0]=f3a1[1]                             # ссылка на текст: name.shtml
    }else aSel[selN]=rmTags(f3s2) # в ПРОЧИХ ячейках - только значение ячейки, очищенное от тегов
    rowLn=substr(rowLn,f3n2)                      # выкусим оптиченную ячейку
    if((selN-dev0>0)&&(selN-dev0-selE<=0)){                 # 21 = dev0+5<=21 17..21
      hitT=aSel[selN]+hitT                        # сразу подсчитаем сумму хитов "за 30 дней"
    }else if(selN-dev0-selE==1){                     # ячейка сразу за границей
      sel1=aSel[selN]
    }else if(selN-dev0-selE>1){                      # прочие ячейки за границей
      sel0=aSel[selN]+sel0
    }
  }
}

function rmTags(f2s1){                            # удаление ВСЕХ тегов в литерале
  while(match(f2s1,/<\/?[a-zA-Z][1-6a-zA-Z]?[a-zA-Z]*[ \/]?[^>]*>/))
      f2s1=substr(f2s1,1,RSTART-1) substr(f2s1,RSTART+RLENGTH)
  return(f2s1)
}

END{                                              # отстрелялись; ну почти...
  if(ARGV[1]==""){
    print("No file in")
    exit                             # защита от случайного пуска
  }
}

    Имя файла скрипта: "cChkH.cmd" V-текст скрипта-V
- - - - - - - - -
@if not defined nJob @echo off
:: cChkH.cmd - просмотр списка *?.lst.bStat [?==v/~], скачивание ВСЕХ текстов из (кривое оставляется!)
:: смотрится дата "Размещён", если она ПОСЛЕ верхней границы выцепляемого интервала - игнорировать!
:: если ПОСЛЕ нижней границы - уточняются фактичкеи хиты в интервале
:: по результатам собирается уточнённый ОТсортированный список *.cChkH (а`ля *.bStat)

:: OS windows - должны быть настроены системные переменные окружения:
:: %time% - HH:MM:SS,tt HH<=9 -> "HH" == " H" лидирующий пробел для часа меньше 10
:: %date% - YYYY.mm.dd

:: 2022.03.31, nvv, годный mod теперь или "v" или "~"
:: ! история изменений - в подвале

echo %date% %time:~0,8% %~nx0 Begs
::  siUrl - основной и резервный URL Самиздата (желательно работать с резервом)
set siUrl=samlib.ru
if exist \siUrl.budclub set siUrl=budclub.ru
::  dnLd - счётчик /формально/ успешно скачанного (НЕ 0b)
set dnLd=0
::  ratW - темп скачивания (секунд на один файл в среднем)
set ratW=0
::  wrks - общее время работы процесса (пуск общего таймера)
for /F %%A in ('gawk.exe "BEGIN{print(systime());exit}"') do set wrks=%%A
:: основные сеты и прочая подготовка к работе...
set errInf=
set nJob=cChkH
set jDat=%date%
::  noReGet - запрет повторного скачивания пустых/битых текстов
set noReGet=%1
::  minT - минимальные допустимые хиты текста (берётся из подвала .v.lst.bStat)
set minT=
::  hitL - хитов за "30дн." (у ТЕКСТА должно быть НЕ меньше), уточняется в *.bStat (по nHit)
set hitL=
::  aChk - скрипт поиска атрибутов текста СИ, может создать очищенную копию
set aChk=%~dp0%nJob%.awk
::  aTxt - скрипт поиска атрибутов текста СИ, может создать очищенную копию
set aTxt=%~dp0dText.awk
set jDat=%date%
::  wDir - общее рабочее место
set wDir=%~d0\Jobs
::  zDir - общий подкаталог для посуточных подкаталогов с итоговыми списками
set zDir=%~d0\Stor\wOld\%jDat%
::  tDir - папка для текстов "наших" 30-дней (like: 2019.08.02.text)
set tDir=
::  jRpt - общий отчёт с итоговыми сводками процессов
set jRpt=%wDir%\_siTop.#
::  nulT текст - недоступен: 0b
set nulT=0
::  badT текст - битый (нет любого): begD,endD,sizT,namA,namT
set badT=0
::  badZ текст с ограниченным доступом
set badZ=0
::  down - хиты изменены
set down=0
::  datJ - верхняя граница интервала дат расчёта (дата в имени файла-списка yyyy.mm.dd.v.lst.bStat)
set datJ=
:: найти 20??.??.??.v.lst.bStat и запомнить его имя (т.е. дату в нём, например, 2019.07.14.v.lst.bStat)
for %%A in (%wDir%\20??.??.??.?.lst.bStat) do set datJ=%%~nA
if NOT defined datJ echo ? NO 20??.??.??.?.lst.bStat found & exit
set tmp0=%datJ:~11,1%
if NOT "%tmp0%" == "~" if NOT "%tmp0%" == "v" (
  echo ? bad mod "%tmp0%" in %datJ%
  goto nExit
)

::  sLst - входной список принятых "начерно" текстов (для скачивания и проверки, дат в первую очередь;)
set sLst=%wDir%\%datJ%.bStat
:: вырежем чистую дату datJ
set datJ=%datJ:~0,10%
::  dBeg - нижняя граница нашего интервала (datJ-29)
set dBeg=mktime(\"%datJ:~0,4% %datJ:~5,2% %datJ:~8,2% 12 00 00\")
for /F %%A in ('gawk.exe "BEGIN{print(strftime(\"%%Y.%%m.%%d\",%dBeg%-86400*29));exit}"') do set dBeg=%%A
::  jDir - место для страниц сканируемых гостевых и рабочих файлов
set jDir=%wDir%\#mRepl
::  jLst - НЕ сортированный список для "siTopTexts" с проверенными хитами за 30дн.
set jLst=%jDir%\]%nJob%.lst
::  eLst - список ПРОБЛЕМ с текстами/гостевыми
set eLst=%wDir%\]%nJob%.bad
::  hold - опорная дата в имени склада с текстами 
set hold=%jDat%
set tDir=%~d0\Stor\Text\%hold%.text
:: если уже собирали именно этот день - запрос "повторить?"
if NOT exist %wDir%\%datJ%.%nJob% goto goOn
set tmp0=
echo %datJ%.%nJob% found; tDir.*\%hold%.text\ reBuild? [yes] == y
set /p tmp0=?[no]?
if /i NOT %tmp0%. == y. echo job canceled {NO to reBuild} & exit
:goOn
if NOT exist %tDir%\ mkDir %tDir%\
if NOT exist %tDir%\ set errInf=fail mkDir %tDir%\ & goto nExit
if NOT exist %jDir%\ mkDir %jDir%\
if NOT exist %jDir%\ set errInf=fail mkDir %jDir%\ & goto nExit
if NOT exist %zDir%\*.* mkDir %zDir%
if NOT exist %zDir%\ set errInf=fail mkDir %zDir%\ & goto nExit
:: очистим будущие списки рейтинга и ошибок
echo. >nul 2> %jLst%
echo. >nul 2> %eLst%
::  tmpF - времянка для скачиваемых текстов
set tmpF=%tDir%\siTextDL.tmp
if exist %tmpF% erase %tmpF%
if errorLevel 1 set errInf=FAIL erase %tmpF%& goto nExit
::  linN - счётчик текстов
set linN=0
:: установим счётчик текстов на максимум...
:: ;2C scan 2021.04.24:dEnd 19719:nEnd  60688:hEnd 30012:nMig 1657:tNum 11:nGis 29154:nLow mod:! OkV:65.7% 
for /F %%A in ('gawk.exe "{if(match($0,/ ([0-9]+):tNum /,a))print(a[1])}" %sLst%') do set linN=%%A
:: ;2B stat 2021.04.26:sEnd  1457:nPre  20102:hPre  1457:miss 300:minT 548:hitL 30075:nHit 801964:hMax last~01:01:31~
for /F %%A in ('gawk.exe "{if(match($0,/ ([0-9]+):hitL /,a))print(a[1])}" %sLst%') do set hitL=%%A
if NOT defined hitL set errInf=fail read 'hitL' (noOrBad in %sLst%) & goto nExit
::  wGetSiz - объём файла, полученного с СИ (default 1b)
set wGetSiz=1
echo req topTexts... linN:%linN%
:: запустим "таймер" качалки
for /F %%A in ('gawk.exe "BEGIN{print(systime());exit}"') do set ratW=%%A
:: hitT hitP numP id_P id_T xDat "namA" "namT" - собственно сборка списка cChkH
for /F "tokens=*" %%A in (
  'gawk.exe "{if(match($1,/^[0-9]+$/))print($0)}" %sLst%'
) do if NOT defined errInf call :reqSiText %%A 
if defined errInf goto nExit
:: остановим "таймер" качалки время работы в секундах
for /F %%A in ('gawk.exe "BEGIN{print(systime()-%ratW%);exit}"') do set ratW=%%A
:: среднее время скачивания файла в секундах
if %dnLd% == 0 (set ratW=0) else (
  for /F %%A in ('gawk.exe "BEGIN{print(sprintf(\"%%3.2f\",%ratW%/%dnLd%));exit}"') do set ratW=%%A
)
if exist %wDir%\*.%nJob% move /y %wDir%\*.%nJob% %jDir% >nul
sort /rec 65535 /r %jLst% > %wDir%\%datJ%.%nJob%
if errorLevel 1 set errInf=FAIL sort %jLst% & goto nExit
:: теперь у нас свежий jLst
set jLst=%wDir%\%datJ%.%nJob%
:: выясним число текстов с хитами НЕ ниже hitL
for /F %%A in ('gawk.exe "{if(%hitL%-$1>0){print(NR-1);exit}}" %jLst%') do set topN=%%A
:: преобразуем секунды в время работы (в обычном формате "чч:мм:сс")
for /F %%A in ('gawk.exe "BEGIN{print(strftime(\"%%H:%%M:%%S\",systime()-%wrks%+46800));exit}"') do set wrks=%%A
:: оформим подвал списка...
echo ;  hitT   oldH.    hitP  numP id_P id_T "namP" "namT" sFil >> %jLst%
:: сначала сводки от aHits и bStat
gawk.exe "{if(match($1,/^;[1-3].$/))print($0)}" %sLst% >> %jLst%
:: затем собственная сводка
rem ;3C chkH dInt:2021.03.23-2021.04.21 topN:767 down:14 nulT:0 badT:1 badZ:0 741:dnLd done:2021.04.24 gap3:16:11-16:16 
set tmp0=;3C chkH ^
dInt~%dBeg%-%datJ%~ ^
%topN%:topN ^
%down%:down ^
%nulT%:nulT ^
%badT%:badT ^
%badZ%:badZ ^
%dnLd%:dnLd ^
%ratW%~ratW ^
%jDat%:done ^
%wrks%~wrks ^
%time:~0,5%
echo %tmp0% >>                  %jLst%
if NOT defined noReGet echo %tmp0% >> %jRpt%
echo %tmp0%
:: да и результат прикопать надлежитЪ
copy /b/y %jLst% %zDir% > nul
if errorlevel 1 set errInf=fail copy %jLst% to %zDir%
:: подудим где нада "готово-с!"
set itog=%jLst%

:nExit
if NOT defined tmpF goto nExit1
if exist %tmpF% erase %tmpF%
:nExit1
if defined errInf echo ! %errInf%
echo %date% %time:~0,8% %~nx0 Ends
goto :eof

:reqSiText работа с очередным тектстом
set /a linN-=1
set linR=       %linN%
set linR=%linR:~-4%
:: 1    2    3    4        5    6        7      8        9
:: hitT hitP numP id_P     id_T "namP"   "namT" sFil     datJ
:: 9241 365732 6  k/kadawr n    Кадавр" "Псион" k~kadawr 2021.04.06

set hitT=%1
set hitP=%2
set numP=%3
set id_P=%4
set id_T=%5
set namP=%6
set namT=%7
set gFil=%8
set curT=      %hitT%
set curT=%curT:~-6%

::  tFil - конечный файл для склада
set tFil=%tDir%\%gFil%.%id_T%.text.html
:: глянем, может этот текст уже у нас...
set tmp0=
for %%J in ( %tFil% ) do set tmp0=%%~zJ
:: пусто == нужно тащить с СИ
if NOT defined tmp0 goto getTxt
:: есть, но нулевой - нужно горхнуть и тащить с СИ
if %tmp0% == 0 (
if defined noReGet goto keepNul
erase %tFil%
goto getTxt
)
:: есть и ненулевой, разберёмся...
set fOut=
for /F "tokens=*" %%J in ('gawk.exe -f %aTxt% %tFil%') do call :chkTxt %%J
:: если OK - займёмся оформлением этого текста
if NOT "%begD%"  == "]" echo. %linR% = %curT%.Text: %gFil%.%id_T% & goto txtInOK
:: если битый, то грохнуть и тащить заново
if defined noReGet goto keepBad
erase %tFil%

:getTxt тащим топовый текст на склад-с
set wUrl=%siUrl%/%id_P%/%id_T%.shtml
if exist %tmpF% erase %tmpF%
echo. %linR% ? %curT%.Text: %gFil%.%id_T%
call wGetUrl2fil.cmd %wUrl% %tmpF%
if defined errInf goto :eof
if NOT %wGetSiz% == 0 goto mark0
:: если файл текста недоступен...
set errT=] err:T nulT: %curT%
move /y %tmpF% %tFil%
if errorLevel 1 set errInf=FAIL move inpF 2 %tFil%& goto :eof
:keepNul
set /a nulT+=1
set typT=%hitT%.
set hitT=1
set namA="x"
set namT="x"
goto fixErr

:mark0 смотрим атрибуты (в т.ч. дату создания, точно название и автор[а/ов]) текста...
set fOut=%tFil%
for /F "tokens=*" %%J in ('gawk.exe -f %aTxt% %tmpF%') do call :chkTxt %%J
set fOut=
:: если свежий текст OK, надо дату вструмить
if NOT "%begD%"  == "]" touch.exe -t %endD:~0,4%%endD:~5,2%%endD:~8,2%0100 %tFil% & goto txtInOK
 :: если с текстом проблемы, ставим на нём крест
move /y %tmpF% %tFil%
if errorLevel 1 set errInf=FAIL move inpF 2 %tFil%& goto :eof
:keepBad
set /a badT+=1
set typT=%hitT%?
set hitT=2

:fixErr с текстом всё плохо...
rem set /a linN-=1
echo.     -X- %errT% %wUrl%
echo %errT% %gFil%.%id_T%.text.html %wUrl% >> %eLst%
touch.exe -t 199001010100 %tFil%
:: и что с того, что брак? Изъятия в перетряхиваемом списке - только вручную!
call :formIt %hitT% %typT% %hitP% %numP% %id_P% %id_T% %namP% %namT% %gFil%
goto :eof

:txtInOK текст как-бы не битый, а доступ?
if %sizT:~-2% == .0 (
  set /a badZ+=1
  set typT=%hitT%z
  set hitT=4
  set errT=] err:Z unAv: %curT%
  goto fixErr
)
:: доступ OK, а хиты текста?
set typT=lowH
if %hitL% GTR %hitT% goto BegDatOk
::  а дата "Размещен:" - ОК?
set typT=:
if %begD% LEQ %dBeg% goto BegDatOk
set typT=%hitT%!
if %begD% GTR %datJ% set hitT=3& goto BegDatOk
set typT=v
:: дата после начала интервала => хиты текста как бы под подозрением...
set filS=U:\Jobs\#bStat\%id_P:~0,1%~%id_P:~2%.stat.htm 
set tmp0=
for /F %%J in ('gawk.exe -f %aChk% %filS%') do set tmp0=%%J
if %hitT% == %tmp0% goto BegDatOk
:: не совпало; наследство отдельно
set typT=%hitT%]
set hitT=%tmp0%
set /a down+=1
:BegDatOk выгрузка собранных данных в накопительный список пефоманс
call :formIt %hitT% %typT% %hitP% %numP% %id_P% %id_T% %namP% %namT% %gFil%
goto :eof

::   9241       :  362225     6 k/kadawr n  "Кадавр" "Псион" k~kadawr
::;  hitT   oldH.    hitP  numP id_P   id_T "namP"   "namT"  sFil 
::   1       2        3     4    5       6     7      8      9

:formIt %hitT% %typT% %hitP% %numP% %id_P% %id_T% %namP% %namT% %gFil%
set typT=        %typT%
set typT=%typT:~-7%
gawk.exe "BEGIN{printf(\"%%7s%%8s%%8s%%6s\",%1,\"%typT%\",%3,%4);exit}" >> %jLst%
echo. %id_P% %id_T% %namA% %namT% %gFil% >>   %jLst%
goto :eof

:chkTxt begD endD sizT namA namT votT jnrT fNam - вычленение данных по тексту
:: . . . 1 .. 2 .. 3 .. 4 .. 5 .. 6 .. 7 .. 8
set begD=%1
set endD=%2
set namA=%4
set namT=%5
set sizT=%3
set votT=%6
set jnrT=%7
set errT=%*
goto :eof

:: 2018.08.10, nvv, creation
:: ...
:: 2020.03.14, nvv, мелкие изменения
:: 2020.06.14, nvv, разделено и реорганизовано. здесь - первая часть, вторая (основная) в siTop-dText.cmd
:: 2020.06.26, minor updates
:: 2020.07.06, minor updates
:: 2020.11.13, nvv, будКлуб - глупость
:: 2020.12.19, nvv, ^- не факт...
:: 2020.12.31, nvv, hitL check added (no datCheck for low text's hits)
:: 2021.01.09, nvv, minor mistake corrected
:: 2021.01.29, nvv, если доступ ограничен (sizT N.0) текст отбраковывается
:: 2021.04.09, nvv, *.bStat turned to *.lst.bStat + minor updates
:: 2021.04.19, nvv, minor updates
:: 2021.04.26, nvv, updates унификация формата передаваемых итоговых значений
:: 2021.05.11, nvv, очередная оптимизация с унификацией...
:: 2021.11.16, nvv, добавлен запрос на пересборку итога, если он уже собирался (защита от случайного пуска)

    Имя файла скрипта: "dText.awk" V-текст скрипта-V
- - - - - - - - -
BEGIN{ # siTop-dText.awk - (для siTop-mText.cmd) взять инфу из текста + очистить сам файл от мусора
#  fOut="nvv.shtml" # при запуске НЕ из CMD, для создания(!) очищенного текста раскомментировать
  fErr="F eInP:NO input file"
  if(ARGV[1]=="")exit                             # защита от случайного пуска
# 2022.08.16 nvv, исправлена неточность в имени файла с текстом /fTxt/ в выдаче скрипта
#  ! прочая история в подвале

# Скрипт выдаёт на StdOut 9 литералов:
# :: begD endD sizT namA namT votT jnrT fNam
# 1  begD(11) - дата размещения текста (2018.04.19)
# 3  endD(11) - дата последнего изменения текста (2018.10.21, "!" == "битое")
# 4  sizT(6)  - размер текста kb (427) + суффикс ".1" (Ok) или ".0" (доступ ограничен)
# 5 "namA"    - ФИО ("Федорочев•Алексей" или "Калашников•Сергей•Александрович,•Андрей_М11")
# 6 "namT"    - заголовок текста ("Мажор")
# 7  votT     - оценки (7.07*811, может быть "0" без кавычек)
# 8 "jnrT"    - жанр(ы) (может быть пусто "0" без кавычек)
# 9  fNam     - чистое ИМЯ файла текста/гостевой (f-fedorochew_a.major)

# ЕСЛИ это проблемный файл, выдача меняется (вместо begD - признак проблемы: "]"):
# ] 190001010100 "err:E erId:description" gFil

#  fDat="190001010100"                             # 1900.01.01 01:00 (12:00 ВЛД)
  begD=0                                          # дата размещения текста
  endD=0                                          # дата последней редакции этого текста
  sizT=-1                                         # объём (в килобайтах) этого текста
  maxL=99                                         #  максимальная допустимая длина ника или заголовка
  namA="-"                                        # автор(ы) текста (as-is)
  namT="-"                                        # заголовок текста (as-is)
  votT=0                                          # оценки текста (like 7.07*811) или диагноз
  jnrT=0                                          # жанр(ы) текста (упаковано, если есть)
  step=0                                      # шаг "разбора" страницы текста/гостевой "Самиздата"
  j1=split(ARGV[1],jArr,":")                      # drv+rest
  j1=split(jArr[j1],jArr,"\\")                    # path+name+ext
  j1=split(jArr[j1],jArr,".")                     # name+ext
  fNam=jArr[1]
  for(j2=2;j2<j1;j2++)fNam=fNam"."jArr[j2]        # a-abwow_a_s.fp.text [.html]
  for ( j2 in ENVIRON ) {                     # ! регистр букв в имени переменной может быть любой
    j1 = tolower( j2 )                            # Temp и temp - РАЗНЫЕ имена для gAwk...
    if ( j1 == "fout" ) fOut=ENVIRON[j2]          # очищенный si-HTML текст
    if ( j1 == "hitt" ) hitT=ENVIRON[j2]          # очищенный si-HTML текст
  }
  if(fOut!=""){                                   #  PATH + id_F +.+ id_T +.+ text.html
    j1=split(fOut,aj,"\\")            #  like: V:\Stor\Text\2022.04.13.text\s~sej_p_s.ms.text.html
    j2=split(aj[j1],aj,".")
    fTxt=aj[1]                       #  чистое имя БЕЗ расширения ".html" like: s~sej_p_s.ms.text
    for(j1=2;j1<j2;j1++)fTxt=fTxt"."aj[j1]
  }else fTxt=fNam
  if(hitT)hitT=sprintf("%7s",hitT)
  fErr="P eAvt:"hitT" NO owner FIO"               # бывает-с, хотя и очень редко...
  flgO=1
  split("",aOut)                                # массив под строки обихоженной страницы с текстом
  aNum=0                                          # счётчик строк в массиве aOut
}

function pack(j1,j0) {          #  заменить мешающие символы на нейтральные + закавычить результат
  j2=""                                           # TAB -> "•"
  while(n1=index(j1,"\x09")) { j2 = j2 substr( j1, 1, n1-1 ) "•"; j1 = substr( j1, n1+1 ) }
  j1=j2 j1
  j2=""                                           # пробел -> "•"
  while(n1=index(j1," ")) { j2 = j2 substr( j1, 1, n1-1 ) "•"; j1 = substr( j1, n1+1 ) }
  j1=j2 j1
  j2=""                                           # неразрывный_пробел -> "•"
  while(n1=index(j1,"\xA0")) { j2 = j2 substr( j1, 1, n1-1 ) "•"; j1 = substr( j1, n1+1 ) }
  j1=j2 j1
  j2=""                                           # двойные кавычки -> "”"
#  while(n1=index(j1,"\"")) { j2 = j2 substr( j1, 1, n1-1 ) "”"; j1 = substr( j1, n1+1 ) }
  while(n1=index(j1,"\"")) { j2 = j2 substr( j1, 1, n1-1 ) "&quot;"; j1 = substr( j1, n1+1 ) }
  j1=j2 j1
  j2=""                                           # "%" -> "&#x25;"
  while(n1=index(j1,"%")) { j2 = j2 substr( j1, 1, n1-1 ) "&#x25;"; j1 = substr( j1, n1+1 ) }
  j1=j2 j1
#   j2=""                                           # "••" -> "•"
#   while(n1=index(j1,"••")) { j2 = j2 substr( j1, 1, n1-1 ); j1 = substr( j1, n1+1 ) }
#   j1=j2 j1
  n0=length(j1)
  if(j0&&(n0-maxL>0))j0=maxL
  else               j0=n0
  n1=split(j1,ak,"•")
  j1=ak[1]
  for(j2=2;j2<=n1;j2++){
    if(j0<length(j1)+1+length(ak[j2])){
      j1=j1"•(nvv:•+"n0-length(j1)"•знаков)"
# •(nvv:•+1•знаков)"
# 123456789.123456789.123456789.
      break
    }
    if(ak[j2]!="")j1=j1"•"ak[j2]
  }
#   if(substr(j1,1,1)=="•") j1 = substr( j1, 2 )    # del "•" at beg
#   n1=length(j1)
#   if(substr(j1,n1,1)=="•") j1 = substr( j1, 1, n1-1 ) # del "•" at end
  return ("\""j1"\"")
}

{                                                 # scan text head (шапка текста)
  outP($0)
# 6 "namA" ФИО ("Федорочев Алексей" или "Калашников Сергей Александрович, Андрей_М11")
# Буревой Андрей: <small><a href=/b/burewojandrej/>другие произведения.</a></small>
# Ил Саган, Кейн Алекс: <small><a href=/s/sagan_i/>другие произведения.</a></small>
  if (!step) {                                    # нулевой шаг - автор
    if (match($0,/^(.+): <small><a href=(.+)>другие произведения\.<\/a><\/small>$/,aj)) {
      namA=pack(aj[1],1)
      id_P=aj[2]
      fErr="H eHdr:"hitT" no title"
      step=1
      next
    }
  }
# 7 "namT" заголовок текста ("Мажор")
# <center><h2>Чего бы почитать?</h2>
  if (step==1) {                                  # первый шаг - название текста
    if (match($0, /^<center><h2>(.+)<\/h2>$/, aj)) {
      namS=aj[1]
      namT=pack(aj[1],1)
      outP("</center>")
      outP("<hr>")
      flgO=0
      fErr="I eInf:"hitT" bad date/size"
      step=2
      next
    }
  }
# 1  endD(11) дата последнего изменения текста (2018.10.21, "!" == "битое")
# 2  begD(11) дата размещения текста (2018.04.19)
# 3  sizT(6)     размер текста kb (427)
# <li>Размещен: 03/09/2018, изменен: 09/11/2018. 410k. <a href=stat.shtml#psv.shtml>Статистика.</a>
# <li>Размещен: 15/08/2003, изменен: 17/02/2009. 246k. <a href="stat.shtml#xu43.shtml">Статистика.</a>
  if (step==2) {                                  # второй шаг - основные атрибуты текста
    if(index($0,"<table width=90% border=0 cellpadding=0 cellspacing=0><tr>")){
      flgO=1
      outP($0)
      next
    }
# if (match($0,/^<li>Размещен: ([0-3][0-9])\/([01][0-9])\/(20)?([0-9]{2})\, изменен: ([0-3][0-9])\/([01][0-9])\/(20)?([0-9]{2})\. ([0-9]+)k\. <a href=.+>Статистика\.<\/a>$/,aj)){
#      begD="20"aj[4]"."aj[2]"."aj[1]
#      endD="20"aj[8]"."aj[6]"."aj[5]
if (match($0,/^<li>Размещен: ([0-3][0-9])\/([01][0-9])\/(..)?([0-9]{2})\, изменен: ([0-3][0-9])\/([01][0-9])\/(..)?([0-9]{2})\. ([0-9]+)k\. <a href=.+>Статистика\.<\/a>$/,aj)){
      if(aj[3]=="")aj[3]="20"
      begD=aj[3] aj[4]"."aj[2]"."aj[1]
      if(aj[7]=="")aj[7]="20"
      endD=aj[7] aj[8]"."aj[6]"."aj[5]
#  fDat=strftime("%Y%m%d%H%M",mktime(aj[6]" "aj[5]" "aj[4]" 01 00 00"))  # YYYYMMDDhhmm (12:00 VLD)
      sizT=aj[9]
      fErr="J eJnr:"hitT" bad janr info"
      step=3
      next
    }
  }
# 9 "jnrT"     - жанр(ы) (может быть пусто "-")
# <li><a href="http://budclub.ru/type/index_type_18-1.shtml">Справочник</a>: <a href="http://budclub.ru/janr/index_janr_20-1.shtml">Естествознание</a>
# <li><a href=/type/index_type_16-1.shtml>Глава</a>: <a href="/janr/index_janr_25-1.shtml">Приключения</a>, <a href="/janr/index_janr_1-1.shtml">Фантастика</a>
  if (step==3) {                            # третий шаг - дополнительные атрибуты текста, жанр(ы)
    if (match($0, /<li>(<a href=[^>]*\/type\/index_type_.+<\/a>: .*)$/, aj)){
      if(aj[1]=="")jnrT=0
      else jnrT=pack(aj[1])
      fErr="V eVot:"hitT" someThing wrongWith votes"
      step=4
      next
    } else if (namT=="\"Информация•о•владельце•раздела\"") {  # тут жанров нет в прнципе!
      jnrT=0
      fErr="V eVot:"hitT" someThing wrongWith votes"
      step=4
      next
    }
  }
# <form action="http://budclub.ru/cgi-bin/votecounter" method=POST>
# <form action="/cgi-bin/votecounter" method=POST>
# <input type=hidden name=FILE value=fant016 >
# <input type=hidden name=DIR value=a/abbakumow_i_n >
# <!-- 1 
  if (step==4) {                                  # четвёртый шаг - оценки...
    if(match($0,/<form action="[^"]*\/cgi-bin\/votecounter\" method=POST>/)){
      step=5
      chkLn=NR+2                                # 3 обязательные строки в начале блока голосования
      next
    }
  }
# 8  votT      - оценки (7.07*811, может быть "0")
# Оценка: <b>6.71*23</b>
# Оценка: <b>5.78*1204</b>
# Оценка: <b><a href=/cgi-bin/vote_show?DIR=b/burewojandrej&FILE=knigi>*</a></b>
# Оценка: <b><a href=/cgi-bin/vote_show?DIR=s/sagan_i&FILE=psv>7.16*117</a></b>
  if (step==5) {                                  # пятый шаг - дополнительные атрибуты текста
    if((NR-chkLn)>0){
      if(index($0,"<!--")){                     # оценки отключены; в тексте больше нечего делать
        step=6
        fErr="T eTxt:"hitT" someThing wrongWith text1"
        next
      }
      if(match($0,/Оценка: <b><a href=\/cgi-bin\/vote_show\?DIR=.+>(.*\*.*)<\/a><\/b>$/,aj)){
        votT=aj[1]
        if(votT=="*") votT=0
        else          votT=pack(votT)
        step=6
        fErr="T eTxt:"hitT" someThing wrongWith text2"
        next
      }
      if (match($0, /Оценка: <b>(.*\*.*)<\/b>$/, aj)) {
        votT=aj[1]
        if(votT=="*") votT=0
        else          votT=pack(votT)
        step=6
        fErr="T eTxt:"hitT" someThing wrongWith text3"
        next
      }
    }
  }
  if (step==6) {                                  # шестой шаг
    if(match($0,/<!--[^>]*Собственно произведение[^>]*-->/))chkLn=NR+4 # nvv 2021.05.14 changed
#     if($0=="<!----------- Собственно произведение --------------->")chkLn=NR+4
    if(NR==chkLn){
      if(index($0,"<h3>Извините, доступ к этому произведению ограничен.</h3>"))sizT=sizT".0"
      else sizT=sizT".1"                          # для обычных текстов, ".0" для -^- хитромудрых
#       print(sprintf("%11s%11s%8.1f %s %s %s %s",begD,endD,sizT,namA,namT,votT,jnrT),fTxt,fNam)
      print(sprintf("%11s%11s%8.1f %s %s %s %s",begD,endD,sizT,namA,namT,votT,jnrT),"",fTxt)
      step=NR
      next
    }
  }
  if ((step-6)>0) {                               # завершающмй шаг
    if(match($0,/<!--[^>]*Блок описания произведения \(слева внизу\)[^>]*-->/)){  #  nvv 2021.05.14
#     if($0=="<!---- Блок описания произведения (слева внизу) ----------------------->")
      flgO=1
#       if(substr($0,RSTART,5)=="<!---")nNew=0
#       else                            nNew=1
#       print nNew,id_P,namP,namT >> "u:\\z.z"
#      outP($0)
#      outP("<hr size=2 noshade>")
      outP("</body>")
      outP("</html>")
      fErr=""
      exit
    }
  }
}

function outP(j1){                                # п/п ПЕРЕсоздания чистой копии входного файла
  if((fOut=="")||(!flgO)) return
  aOut[++aNum]=j1
}

END{
  if(fErr!=""){
    print("] err:"fErr,fTxt,ARGV[1])           # у нас проблема...
    exit
  }
  for(j1=1;j1<=aNum;j1++)print(aOut[j1])>fOut
# 2018.02.25, nvv, creation (siReplsChk.awk)
# ...
# 2019.01.04, nvv, update
# 2019.04.04, nvv, sizB added (== ".0" if "доступ ... ограничен", else == ".1")
# 2019.07.04, nvv, работаеи ТОЛЬКО с текстами
# 2019.08.05, nvv, update
# 2019.12.06, nvv, current unification
# 2020.06.13, nvv, обрабатываемая страница буфферизируется; выдача - только если всё ОК
# 2021.05.15, nvv, иначе (с доп.пробелами) оформлены некоторые HTML-комментарии у части текстов
#     пришлось менять метод распознания: независящий от использованного варианта оформления оных
# 2021.09.29, nvv, символ 0x1A (Ctrl+Z) - для gAwk == "конец файла" (даже если это не так)
# 2022.03.09, nvv, уточнён алгоритм вычленения данных в служебке (при разной подработке текста)
# 2022.04.13 nvv, теперь (fOut) в выдаче указывается конечное ИМЯ файла ПОСЛЕ преобразования
# 2022.06.29 nvv, введено ограничение maxL длины ника и заголовка текста (защита от идиотов)
}

    Имя файла скрипта: "dText.cmd" V-текст скрипта-V
- - - - - - - - -
@if not defined nJob @echo off
:: dText.cmd - сборка и оснащение 30d списка 1k топовых текстов доп.данными о них и их гостевых
::  основной и резервный URL Самиздата (желательно работать с резервом)
:: %time% - HH:MM:SS,tt HH<=9 -> "HH" == " H" лидирующий пробел для часа меньше 10
:: %date% - YYYY.mm.dd

:: 2021.11.16, nvv, добавлен запрос на пересборку итога, если он уже собирался (защита от случайного пуска)
:: ! история изменений - в подвале

echo %date% %time:~0,8% %~nx0 Begs
::  siUrl - основной и резервный URL Самиздата (желательно работать с резервом)
set siUrl=samlib.ru
if exist \siUrl.budclub set siUrl=budclub.ru
::  dnLd - счётчик /формально/ успешно скачанного (НЕ 0b)
set dnLd=0
::  ratW - темп скачивания (секунд на один файл в среднем)
set ratW=0
::  wrks - общее время работы процесса (пуск общего таймера)
for /F %%A in ('gawk.exe "BEGIN{print(systime());exit}"') do set wrks=%%A
:: основные сеты и прочая подготовка к работе...
set errInf=
set nJob=dText
set mark=;m
set awkV=%~dp0%nJob%V.awk
set awkR=%~dp0%nJob%R.awk
set awkT=%~dp0%nJob%.awk
set jDat=%date%
::  wDir - общее рабочее место
set wDir=%~d0\Jobs
::  sDir - общее складское хранилище
set sDir=%~d0\Stor
set jRpt=%wDir%\_siTop.#
::  dirT - склад для топовых текстов (лидеры рейтингов)
set dirT=%sDir%\Text
::  tHit - сумма всех хитов призёров, попавших в тор1000
set tHit=0
:: счётчики проблем...
set errInf=
::  nulG гостевая - недоступна: 0b
set nulG=0
::  badG гостевая - битая (нет любого на проверяемых страницах): endD,sizT
set badG=0
::  rNot гостевая - пуста (нет любого): Dat,rNum
set rNot=0
::  zipR гостевая - архив(ы)
set zipR=0
::  datJ - верхняя граница интервала дат расчёта (дата в имени файла-списка yyyy.mm.dd.v.lst.bStat)
set datJ=
:: найти 20??.??.??.v.lst.bStat и запомнить его имя (т.е. дату в нём, например, 2019.07.14.v.lst.bStat)
for %%A in (%wDir%\20??.??.??.?.lst.bStat) do set datJ=%%~nA
if NOT defined datJ echo ? NO 20??.??.??.?.lst.bStat found & exit
set tmp0=%datJ:~11,1%
if NOT "%tmp0%" == "~" if NOT "%tmp0%" == "v" (
  echo BAD mod "%tmp0%" in %datJ%
  exit
)
set datJ=%datJ:~0,10%
set cLst=%wDir%\%datJ%.cChkH
:: найти последний 20??.??.??.cChkH и запомнить его имя (т.е. дату в нём, например, 2019.07.14.cChkH)
if NOT exist %cLst% echo NO %cLst% found, exit & exit
:: если уже собирали именно этот день - запрос "повторить?"
if NOT exist %wDir%\%datJ%.%nJob% goto goOn
set tmp0=
echo %datJ%.%nJob% found; reBuild it? [yes] == y
set /p tmp0=?[no]?
if /i NOT %tmp0%. == y. echo job canceled {NO to reBuild} & exit
:goOn  jDir - место для страниц сканируемых гостевых и рабочих файлов
set jDir=%wDir%\#mRepl
if NOT exist %jDir%\ mkDir %jDir%\
if NOT exist %jDir%\ set errInf=fail mkDir %jDir%\ & goto nExit
:: если итоговый список уже принят "к публикации" (суффикс "#"), трогать низя
if exist %wDir%\%datJ%.#.%nJob% set errInf=found %wDir%\%datJ%.#.%nJob% (published?!) & goto nExit
:: если есть пока непринятый итоговый список, он берётся как опорный для пересборки
if exist %wDir%\%datJ%.%nJob% move /y %wDir%\%datJ%.%nJob% %jDir%
::  lstJ - список для "siTopTexts" - популярные тексты за 30 дней (со всеми атрибутами)
set lstJ=%jDir%\%datJ%.%nJob%
::  eLst - несортированный список ПРОБЛЕМ с текстами/гостевыми
set eLst=%wDir%\]%nJob%.bad
::  hold - опорная дата привязки реплик
set hold=
:: если перезапуск (есть lstJ) - считать дату привязки, иначе - проверить почистить jDir
if exist %lstJ% (
  for /F %%J in ('gawk.exe "{if(match($1,/^%mark%$/)){print($3);exit}}" %lstJ%') do set hold=%%J
) else (
  if exist %jDir%\*.* erase /F/Q %jDir%\*.*
  if errorlevel 1 set errInf=fail erase %jDir%\*.* & goto nExit
  set hold=%jDat%
)
:: собирать список должно СЕГОДНЯ! Завтра (если не успели) - всё с начала
if NOT %hold%. == %jDat%. (
  set hold=%jDat%
  if exist %jDir%\*.* erase /F/Q %jDir%\*.*
  if errorlevel 1 set errInf=fail erase %jDir%\*.* & goto nExit
)
:: очистим будущие списки рейтинга и ошибок
echo %mark% hold %hold% >           %lstJ% 2> %eLst%
::  tDir - папка для топовых текстов "наших" 30-дней (like: 2019.08.02.text)
set tDir=%dirT%\%hold%.text
:: должОн бысть (после cChkH.cmd)
if NOT exist %tDir%\ set errInf=%tDir% not found & goto nExit
::  tmpF - времянка для скачиваемых текстов
set tmpF=%tDir%\siTextDL.tmp
if exist %tmpF% erase %tmpF%
if errorLevel 1 set errInf=FAIL erase %tmpF%& goto nExit
::  ok1k - устанавливается, когда оптичены все принятые топовые тексты (+оценки +гостевые к ним)
set ok1k=
::  linN - счётчик текстов
set linN=0
::  wGetSiz - объём файла, полученного с СИ (default 1b)
set wGetSiz=1
echo req topTexts...
:: запустим "таймер" качалки
for /F %%A in ('gawk.exe "BEGIN{print(systime());exit}"') do set ratW=%%A
:: hitT hitP numP id_P id_T xDat "namA" "namT"
for /F "tokens=*" %%A in (%cLst%) do if NOT defined errInf if NOT defined ok1k call :reqSiText %%A 
if defined errInf goto nExit
:: остановим "таймер" качалки время работы в секундах
for /F %%A in ('gawk.exe "BEGIN{print(systime()-%ratW%);exit}"') do set ratW=%%A
:: среднее время скачивания файла в секундах
if %dnLd% == 0 (set ratW=0) else (
  for /F %%A in ('gawk.exe "BEGIN{print(sprintf(\"%%3.2f\",%ratW%/%dnLd%));exit}"') do set ratW=%%A
)
:: jLst - конечный список-итог
set jLst=%wDir%\%datJ%.%nJob%
copy /b/y %lstJ% %jLst% > nul
if errorlevel 1 set errInf=fail copy %lstJ% to %wDir%
:: преобразуем секунды в время работы (в обычном формате "чч:мм:сс")
for /F %%A in ('gawk.exe "BEGIN{print(strftime(\"%%H:%%M:%%S\",systime()-%wrks%+46800));exit}"') do set wrks=%%A
:: оформим подвал списка...
::  .   3333   159   49066  1821.0  -993  993 2014.07.10 2017.11.30 2018.01.26 "Скс." "Режим•бога" s/sks 111 "6.05*126" 0 "Скс" 10_3834 s-sks.111 
rem echo ; hitT.1 numP.2 hitP.3 sizT.4 aNum.5 rNum.6 begD.7    endD.8     aDat.9   ^
rem "namA".10 "namT".11 id_P.12 id_T.13 "votT".14 "jnrT".15 gFil.16 >> %jLst%
set tmp0=; hitT.1 numP.2 hitP.3 sizT.4 aNum.5 rNum.6 begD.7    endD.8     aDat.9
set tmp0=%tmp0%   "namA".10 "namT".11 id_P.12 id_T.13 "votT".14 "jnrT".15 gFil.16
echo %tmp0% >> %jLst%
:: затем сводки от aHits и cChkH
gawk.exe "{if(match($1,/^;[1-3].$/))print($0)}" %cLst% >> %jLst%
:: ;4T text nulG:0 badG:0 rNot:85 zipR:253 1456:dnLd done:2019.11.28 17:17-17:31 <- штат
:: затем собственная сводка
set tmp0=;4T text ^
%nulG%:nulG ^
%badG%:badG ^
%rNot%:rNot ^
%zipR%:zipR ^
%tHit%:tHit ^
%dnLd%:dnLd ^
%ratW%~ratW ^
%jDat%:done ^
%wrks%~wrks ^
%time:~0,5%
echo %tmp0% >>                  %jLst%
echo %tmp0% >>                  %jRpt%
echo %tmp0%
:: подудим где нада "готово-с!"
set itog=%jLst%

:nExit
if exist %tDir%\siTextDL.tmp erase %tDir%\siTextDL.tmp
if defined errInf echo !!! %errInf%
echo %date% %time:~0,8% %~nx0 Ends
goto :eof

:reqSiText работа с очередным тектстом и его гостевой
if defined ok1k goto :eof
set hitT=%1
set oldH=%2
:: всё годно было ДО lowH дальше не интересно
if %oldH%. == lowH. (set ok1k=yes & goto :eof)
set /a linN+=1
set linR=       %linN%
set linR=%linR:~-4%
:: 1    2    3      4    5    6     7      8      9
:: hitT oldH hitP   numP id_P id_T "namP" "namT"  sFil 
:: 9241    : 362225 6 k/kadawr n "Кадавр" "Псион" k~kadawr 

set hitP=%3
set numP=%4
set id_P=%5
set id_T=%6
set namP=%7
set namT=%8
set gFil=%id_P:~0,1%~%id_P:~2%.%id_T%
set curT=      %hitT%
set curT=%curT:~-6%

::  tFil - конечный файл для склада (дОлжен быть!)
set tFil=%tDir%\%gFil%.text.html
if NOT exist %tFil% set errInf=no %tFil% found & goto :eof
set tmp0=
for %%J in ( %tFil% ) do set tmp0=%%~zJ
:: есть, но нулевой - нужно горхнуть и тащить с СИ
if %tmp0% == 0 set errInf=zeroSize %tFil% found & goto :eof
:: есть и ненулевой, разберёмся...
set fOut=
for /F "tokens=*" %%J in ('gawk.exe -f %awkT% %tFil%') do call :chkTxt %%J
:: если OK - займёмся оформлением этого текста
if "%begD%"  == "]" set errInf=bad %tFil% found & goto :eof
echo. %linR% = %curT%.Text: %gFil%

:: txtInOK теперь оценки, если есть-с... ;) budclub.ru/cgi-bin/vote_show?DIR=n/nosow_w_w&FILE=si_tq1k-curr
if exist %tDir%\siTextDL.tmp erase %tDir%\siTextDL.tmp
if %votT% == 0 goto votesOK
set wUrl="%siUrl%/cgi-bin/vote_show?DIR=%id_P%&FILE=%id_T%"
set tmpF=%jDir%\%gFil%.vote.htm
if exist %tmpF% (
echo.     =Votes
goto voteIs
)
echo.     ? Votes
call wGetUrl2fil.cmd %wUrl% %tmpF%
if defined errInf goto :eof
:: если файл оценок недоступен => текст удалить
set errT=] err:V badV %curT% %gFil%.text
if %wGetSiz% == 0  set /a nulT+=1 & goto killZero
:voteIs проверяем полученное
set chkN=0
for /F "tokens=1-4*" %%J in ('gawk.exe -f %awkV% %tmpF%') do (
set balN=%%J& ^
set balM=%%K& ^
set balS=%%L& ^
set balD=%%M) 
:: если с оценками проблема => текст удалить
if "%balN%"  == "]]" set /a nulT+=1 & goto killZero
set votT="%balM%»%balS%*%balN%"

:votesOK теперь гостевая этого текста; страница самых последних реплик
rem !!! 2019.11.25, nvv NO guestBooks == NO scanning them atALL
rem if defined noRepl goto noRepl
rem !!!
set wUrl=%siUrl%/comment/%id_P%/%id_T%
set tmpF=%jDir%\%gFil%.z00.z1.repl.htm
if exist %tmpF% (
echo.     =Repls
goto replIs
)
echo.     ? Repls
call wGetUrl2fil.cmd %wUrl% %tmpF%
if defined errInf goto :eof
:: если файл гостевой недоступен...
set errT=] err:N nulG %curT% %gFil%.z00.z1.repl
if %wGetSiz% ==  0  set /a nulG+=1 & goto killZero
:replIs проверяем полученное
set chkN=0
for /F "tokens=*" %%J in ('gawk.exe -f %awkR% %tmpF%') do call :chkRpl %%J
:: если с гостевой текста проблемы, ставим на тексте крест
if "%dat0%"  == "]" set /a badG+=1 & goto fixErr
:: если гостевая пуста, так и запишем...
if %dat0% == 0 goto noRepl
set aDat=%dat0%
set arcN=%num0%
set rNum=%num1%
set pagN=%num2%
:: если ОДНА реплика, без архивов; всё ясно БЕЗ второго запроса
if "%arcN%%rNum%" == "01" (
  set aNum=%rNum%
  goto fixNews
)
:: архива нет, реплик: 2..1012 == НЕ одна реплика, нужен запрос самой первой в текущих
if %arcN% == 0 goto reqCur
:: == архив(!) запрос архива
set tmp0=0%pagN%
set tmp2=00%arcN%
set wUrl="%siUrl%/comment/%id_P%/%id_T%.%arcN%?ORDER=reverse&PAGE=%pagN%"
set tmpF=%jDir%\%gFil%.%tmp2:~-3%.%tmp0:~-2%.repl.htm
if exist %tmpF% (
echo.     =Arch
goto replA
)
echo.     ? Arch
call wGetUrl2fil.cmd %wUrl% %tmpF%
if defined errInf goto :eof
:: если файл гостевой недоступен...
set errT=] err:N nulG %curT% %gFil%.%tmp2:~-3%.%tmp0:~-2%.repl
if %wGetSiz% ==  0  set /a nulG+=1 & goto killZero
:replA ищем в последнем архиве реплику с тем же номером (или радом;)
set chkN=%rNum%
for /F "tokens=*" %%J in ('gawk.exe -f %awkR% %tmpF%') do call :chkRpl %%J
:: если с гостевой текста проблемы, ставим на тексте крест
if "%dat0%"  == "]" set /a badG+=1 & goto fixErr
:: если гостевая пуста, так и запишем...
if %dat0% == 0 goto noRepl
:: - архив(ы) сосчитаем
set /a zipR+=1
:: pref "-" == number is IN archive
set aNum=-%num1%
set aDat=%dat0%
goto fixNews

:reqCur дата первой реплики в текущих
set wUrl="%siUrl%/comment/%id_P%/%id_T%?ORDER=reverse&PAGE=1"
set tmpF=%jDir%\%gFil%.z00.01.repl.htm
if exist %tmpF% (
echo.     =Curr#1
goto repl1
)
echo.     ? Curr#1
call wGetUrl2fil.cmd %wUrl% %tmpF%
if defined errInf goto :eof
set errT=] err:N nulG %curT% %gFil%.z00.01.repl
:: если файл гостевой недоступен...
if %wGetSiz% ==  0  set /a nulG+=1 & goto killZero
:repl1
set chkN=1
for /F "tokens=*" %%J in ('gawk.exe -f %awkR% %tmpF%') do call :chkRpl %%J
:: если с гостевой текста проблемы, ставим на тексте крест
if "%dat0%"  == "]" set /a badG+=1 & goto fixErr
:: если гостевая пуста, так и запишем...
if %dat0% == 0 goto noRepl
set aDat=%dat0%
set aNum=%num1%
goto fixNews

:noRepl  гостевая пуста, отклик по нулям
set /a rNot+=1
set aDat=         0
set aNum=0
set rNum=0
:fixNews выгрузка собранных данных в накопительный список
set /a tHit+=%hitT%
call :formIt %hitT% %numP% %hitP% %sizT% %aNum% %rNum%
goto :eof

:formIt %hitT% %numP% %hitP% %sizT% %aNum% %rNum%
gawk.exe "BEGIN{printf(\"%%7s%%6s%%8s%%8.1f%%6s%%5s\",%1,%2,%3,%4,%5,%6);exit}" >> %lstJ%
echo. %begD% %endD% %aDat% %namA% %namT% %id_P% %id_T% %votT% %jnrT% %oldH% %gFil% >> %lstJ%
goto :eof

:chkTxt begD endD sizT namA namT votT jnrT fNam - вычленение данных по тексту/гостевой
:: . . . 1 .. 2 .. 3 .. 4 .. 5 .. 6 .. 7 .. 8
set begD=%1
set endD=%2
set namA=%4
set namT=%5
set sizT=%3
set votT=%6
set jnrT=%7
set errT=%*
goto :eof

:chkVot %%J balN balM balS 
set balN=%1
set balM=%2
set balS=%4 
goto :eof

:chkRpl 1 2 .. 3 .. 4 .. 5 - вычленение данных по тексту/гостевой
:: . aDat arcN aNum pagN fNam
:: . dat0 num0 num1 num2
set dat0=%1
set num0=%2
set num1=%3
set num2=%4
set errT=%*
goto :eof

:killZero
if exist %tmpF% erase %tmpF%
:fixErr с текстом или его гостевой всё плохо, пропускаем (игнорируем этот текст)
set /a linN-=1
echo.     -X- %errT%
echo %errT% %wUrl% >> %eLst%
goto :eof

:: 2018.08.10, nvv, creation
:: ...
:: 2020.01.08, nvv, добавлен счётчик tHit - подсчёт суммы хитов всех призёров
:: 2020.03.14, nvv, мелкие изменения
:: 2020.06.14, nvv, работа не с *.bStat а с *.cChkH <- список с нормализованными хитами за 302 дней
:: 2020.08.08, nvv, подправлен оживляж (нагляднее-с;)
:: 2020.11.16, nvv, будКлуб - глупость? не факт... ;(
:: 2020.12.31, nvv, hitL check added (no votes & repls for low text's hits)
:: 2021.04.09, nvv, minor updates
:: 2021.04.19, nvv, minor updates
:: 2021.05.11, nvv, очередная оптимизация с унификацией...

    Имя файла скрипта: "dTextR.awk" V-текст скрипта-V
- - - - - - - - -
BEGIN{            # siTop-dTextR.awk - (для рейтингов T1000) взять инфу со страниц гостевой текста
  fErr="] eInP:NO input file"
  if(ARGV[1]=="")exit                             # защита от случайного пуска
# 2018.02.25, nvv, creation (siReplsChk.awk)
# ...
# 2019.01.04, nvv, update
# 2019.04.04, nvv, sizB added (== ".0" if "доступ ... ограничен", else == ".1")
# 2019.07.28, nvv, переименовано + убрана работа с текстами, - только гостевые + реорганизовано
# 2019.12.06, nvv, current unification

# Скрипт выдаёт на StdOut 5 литералов:
#:: aDat,arcN,rNum,pagN,gFil
#1 aDat - дата нужной/последней реплики в интервале, в гостевой (2018.07.20)
#2 arcN - кол-во архивов (0..999)
#3 rNum - номер нужной/последней реплики в интервале, в гостевой (0..1012, может быть пусто "-")
#4 pagN - номер нужной/последней страницы гостевой (1..26)
#5 gFil - чистое ИМЯ файла текста/гостевой (f-fedorochew_a.major)

  arcN=0                                          # кол-во архивов (значения по умолчанию)
  pagN=1                                      # кол-во страниц, pageN==1 is default, ever if none
  rNum=0                                          # номер найденной реплики
  aDat=0                                          # дата этой найденной реплики
  step=0                                      # шаг "разбора" страницы текста/гостевой "Самиздата"
  modSI=1                         # ОБРАТНАЯ сортировка реплик на странице гостевой (штатная в СИ)
  chkN=""           # 0==последняя реплика; >0==номер искомой реплики; ""(пусто) - атрибуты текста
  gFil=""                                         # имя файла, БЕЗ пути и расширения
  for ( tmp0 in ENVIRON ) {                   # ! регистр букв в имени переменной может быть любой
    tmp1 = tolower( tmp0 )                        # Temp и temp - РАЗНЫЕ имена для gAwk...
    if ( tmp1 == "chkn" ) chkN=ENVIRON[tmp0]      # номер первой реплики в последней 1000-е
    if ( tmp1 == "gfil" ) gFil=ENVIRON[tmp0] #чистое ИМЯ файла текста/гостевой (f-fedorochew_a.major)
    if ( tmp1 == "hitt" ) hitT=ENVIRON[tmp0]      # очищенный si-HTML текст
  }
  if(hitT)hitT=sprintf("%7s",hitT)
  fErr="P eAvt:"hitT" NO owner FIO"               # бывает-с, хотя и очень редко...
  if(gFil==""){                               # при автономной работе приходится вычленять нужное
    jr=split(ARGV[1],jArr,":")                    # drv
    jr=split(jArr[jr],jArr,"\\")                  # path
    tmp1=split(jArr[jr],jArr,".")                 # name+ext
    gFil=jArr[1]
    for(jr=2;jr<tmp1;jr++)gFil=gFil"."jArr[jr]    # a-abwow_a_s.fp.z00.01.repl.htm
  }
}

function getDat(tmp2) {                           # выдернуть дату реплики
#"<small>91.</small> <b>Алена Николаева</b> <small><i>2018/07/03 12:23  </i>  ") {
  if (match(tmp2, /([0-9]{4})\/([0-9]{2})\/([0-9]{2}) ([0-9]{2}:[0-9]{2}) *<\/i>/,aj)) { 
  return aj[1]"."aj[2]"."aj[3];                   # aj[4] игнорируем, только дата, время не нужно
  } else {return "~???~"}; 
}

function pack(j1) {             # заменить мешающие символы на нейтральные + закавычить результат
  j2=""                                           # TAB -> "•"
  while(n=index(j1,"\x09")) { j2 = j2 substr( j1, 1, n-1 ) "•"; j1 = substr( j1, n+1 ) }
  j1=j2 j1
  j2=""                                           # пробел -> "•"
  while(n=index(j1," ")) { j2 = j2 substr( j1, 1, n-1 ) "•"; j1 = substr( j1, n+1 ) }
  j1=j2 j1
  j2=""                                           # неразрывный_пробел -> "•"
  while(n=index(j1,"\xA0")) { j2 = j2 substr( j1, 1, n-1 ) "•"; j1 = substr( j1, n+1 ) }
  j1=j2 j1
  j2=""                                           # двойные кавычки -> "”"
#  while(n=index(j1,"\"")) { j2 = j2 substr( j1, 1, n-1 ) "”"; j1 = substr( j1, n+1 ) }
  while(n=index(j1,"\"")) { j2 = j2 substr( j1, 1, n-1 ) "&quot;"; j1 = substr( j1, n+1 ) }
  j1=j2 j1
  j2=""                                           # "••" -> "•"
  while(j0=index(j1,"%")) { j2 = j2 substr( j1, 1, j0-1 ) "&#x25;"; j1 = substr( j1, j0+1 ) }
  j1=j2 j1
  j2=""                                           # "••" -> "•"
  while(n=index(j1,"••")) { j2 = j2 substr( j1, 1, n-1 ); j1 = substr( j1, n+1 ) }
  j1=j2 j1
  if(substr(j1,1,1)=="•") j1 = substr( j1, 2 )    # del "•" at beg
  n=length(j1)
  if(substr(j1,n,1)=="•") j1 = substr( j1, 1, n-1 ) # del "•" at end
  return ("\""j1"\"")
}

{
# <h3>Влад
  if (!step) {                                    # нулевой шаг - автор
    if (match($0, /<h3>(.*)$/, aj)) {
      if(aj[1]=="") exit                          # == "битая" гостевая (нет ника автора)
      fErr="H eHdr:"hitT" no title"
      step=1
      next
    }
  }
# <center><h2>Комментарии: <a href=
#<h2>Комментарии: <a href="/k/koshienko_a_g/kniga3.shtml">6 Косплей Сергея Юркина (книга четвёртая)</a><br><small>&nbsp;()</small></h2>
#<center><h2>Комментарии: <a href="/f/fedorochew_a/2018.shtml">Лось</a><br><small>&nbsp;
#(Оценка:<b><a href=/cgi-bin/vote_show?DIR=f/fedorochew_a&FILE=2018>8.26*694</a></b>,)</small></h2>
#        <center><h2>Комментарии: <a href="/p/polishuk_w/zenitchic.shtml">Зенитчик</a>
#<br><small>&nbsp;(Оценка:<b><a href=/cgi-bin/vote_show?DIR=p/polishuk_w&FILE=zenitchic
#>5.98*169</a></b>,)</small></h2>
#  {if(match($0,/:<b><a href=\/cgi-bin\/vote_show\?DIR=.+>(.+)<\/a>/,a)) print a[1];exit}
  if (step==1) {
    if (tmp0=index($0,"<h2>Комментарии: <a href=")) {
      $0=substr($0,tmp0+30)
      match($0, />(.*)<\/a><br>/, aj)             # выдернуть название текста, к которому гостевая
      namT=pack(aj[1])
      if ((aj[1]=="")||(aj[1]=="Управление")) exit #== "битая" гостевая (нет или "левое" название)
      fErr="I eInf:"hitT" bad dates/size"
      step=2
      next
    }
  }
#<li>Обновлено: 21/05/2018. 1123k. <a
#<li>Размещен: 30/04/2019, изменен: 29/06/2019. 231k. <a
  if (step==2) {
if (match($0,/^<li>Размещен: [0-9]{2}\/[0-9]{2}\/(..)?[0-9]{2}\, изменен: [0-9]{2}\/[0-9]{2}\/(..)?[0-9]{2}\. [0-9]+k\. <a/,aj)){
      fErr="J eJnr:"hitT" bad janr info"
      step=3
      next
    }
  }
#<li><a href=/type/index_type_15-1.shtml>Новелла</a>: <a href="/janr/index_janr_5-1.shtml">Проза</a>, <a href="/janr/index_janr_1-1.shtml">Фантастика</a>, <a href="/janr/index_janr_35-1.shtml">Фанфик</a>
#<li><a href=/type/index_type_5-1.shtml>Статья</a>: <a href="/janr/index_janr_15-1.shtml">Философия</a>, <a href="/janr/index_janr_10-1.shtml">Переводы</a>
#<li><a href=/type/index_type_4-1.shtml>Очерк</a>: 
  if (step==3) {
    if(match($0, /<li><a href=\/type\/index_type_.+<\/a>: /, aj)){
      fErr=""
      step=4
      next
    }
    if (namT=="\"Информация•о•владельце•раздела\"") { # тут жанров нет в прнципе!
      fErr=""
      step=4
      next
    }
  }
#6 arcN["namA"] кол-во архивов (0..999)
#7 pagN["namT"] номер нужной/последней страницы гостевой (1..26)
#<center><small> Отсортировано по:[убыванию][<a href=/comment/k/koshienko_a_g/kniga3?ORDER=reverse>возрастанию</a>]</small></center><center><b>Страниц (8):</b> <b>1</b> <a href=/comment/k/koshienko_a_g/kniga3?PAGE=2>2</a> <a href=/comment/k/koshienko_a_g
# … <b>Страниц (2) …
# … <b>Архивы (46) …
  if ((step-3)>0) {     # навигация есть ТОЛЬКО если ВОЗМОЖНА пересортировка (даже невидимого-с;)
    if ( index( $0, "> Отсортировано по:[" )) {   # в СИ (по умолчанию) сортировка по убыванию,
      if ( match($0,/<b>Страниц \(([0-9]{1,2})\)/, aj ) ) {
        pagN=aj[1]
        if ( match($0,/<b>Архивы \(([0-9]+)\)/, aj ) ) {
          arcN=aj[1]
        }
      }
      if ((pagN-26)>0) pagN=26
      modSI=index($0," по:[убыванию") # признак сортировки: 0==прямая, >0==обратная (по умолчанию)
    }
#1  aDat(11)    дата нужной/последней реплики в интервале, в гостевой (2018.07.20)
#4 rNum(5) - номер нужной/последней реплики в интервале, в гостевой (0..1012, может быть пусто "-")
#"<small>91.</small> <b>Алена Николаева</b> <small><i>2018/07/03 12:23  </i>  ") {
    if ( match($0,/<small>([0-9]+)\.<\/small>/, aj ) ) {
      step++
      rNum=aj[1]                                  # номер очередной реплики ("91")
      aDat=getDat($0)                             # дата очередной реплики ("2018,07,03)
      if(modSI) exit
      else {
        if (!chkN) exit
        else if ((rNum-chkN)>=0) exit             # первая в границах
      }
    }
  }
}

END{
  if(fErr!="") print("] err:"fErr,fNam)           # у нас проблема
  print(sprintf("%11s%5s%5s%3s",aDat,arcN,rNum,pagN),gFil)
}

    Имя файла скрипта: "dTextV.awk" V-текст скрипта-V
- - - - - - - - -
BEGIN{            # siTop-dTextV.awk - (для рейтингов T1000) взять инфу со страницы оценок текста
  fErr="]] NO or bad file"
  if(ARGV[1]=="")exit                             # защита от случайного пуска
# 2019.12.08, nvv, creation
  gFil=""                                         # имя файла, БЕЗ пути и расширения
  for ( tmp0 in ENVIRON ) {                   # ! регистр букв в имени переменной может быть любой
    tmp1 = tolower( tmp0 )                        # Temp и temp - РАЗНЫЕ имена для gAwk...
    if ( tmp1 == "gfil" ) gFil=ENVIRON[tmp0] #чистое ИМЯ файла текста/гостевой (f-fedorochew_a.major)
  }
  if(gFil==""){                               # при автономной работе приходится вычленять нужное
    jr=split(ARGV[1],jArr,":")                    # drv
    jr=split(jArr[jr],jArr,"\\")                  # path
    tmp1=split(jArr[jr],jArr,".")                 # name+ext
    tmp1--
    gFil=jArr[1]
    for(jr=2;jr<tmp1;jr++)gFil=gFil"."jArr[jr]    # a-abwow_a_s.fp.z00.01.repl.htm
  }
}

# budclub.ru/cgi-bin/vote_show?DIR=n/nosow_w_w&FILE=si_tq1k-curr
# budclub.ru/cgi-bin/vote_show?DIR=m/metelxskij_n_a&FILE=wtt-1

#голосования.<br><table align=right cellspacing=0 cellpadding=10 border=0 bgcolor=#e0e0e0>
#<tr><td valign=top><b>ВСЕГО: 8.00 * 3</b>
#</br><b>8</b>: 2 (отличная книга)
#<br><b>6</b>: 1 (нормально)
#<br></td>
#<tr><td colspan=2 bgcolor=#e5e5e5>Сред. <b>7.33</b> Дисп. <b>0.89</b>
#<br><br>
#Всего оценок: 1585376</a><br><a href=/rating/top40/>Рейтинг: Сорок лучших</a><br><a href=/rating/top100/>Рейтинг: Топ-500</a><br><a href=/rating/expert/>Рейтинг: Избранное</a></td></tr></td></table><pre><small> <a href=http://samlib.ru/cgi-bin/vote_show?

#голосования.<br><table align=right cellspacing=0 cellpadding=10 border=0 bgcolor=#e0e0e0>
#<tr><td valign=top><b>ВСЕГО:  * 2</b>
#</br><b>8</b>: 1 (отличная книга)
#<br><b>7</b>: 1 (хорошая книга)
#<br></td><tr><td colspan=2 bgcolor=#e5e5e5>Сред. <b>7.50</b> Дисп. <b>0.25</b>. Cnt <b>2</b>
#<br><br>Всего оценок: 1584436</a><br><a href=/rating/top40/>Рейтинг: Сорок лучших</a><br><a href=/rating/top100/>Рейтинг: Топ-500</a><br><a href=/rating/expert/>Рейтинг: Избранное</a></td></tr></td></table><pre><small>  <a href=http://samlib.ru/cgi-bin/vote_show?ITEM=seVBNdpdUckiE&DIR=n/nilin_a&FILE=plenniki1>66.102.x.xx</a>        8  2019/10/28 10:07:31 Mozilla/5.0 

{                             # вычленение итогов по оценкам: СИ_баллы; кол-во; среднее; дисперсия
  if(match($0,/=top><b>\
ВСЕГО: ([0-9]+)?\.?([0-9]+)? \* ([0-9]+)<\/b>.+>\
Сред. <b>([0-9]+)\.([0-9]+)<\/b> \
Дисп. <b>([0-9]+)\.([0-9]+)\
.+<br><br>/,aj)){
    if(!aj[1])aj[1]=0                             # оценок меньше 3-х, балловСИ просто нет
    if(!aj[2])aj[2]=0                             # значит ставим 0.0 вместо
    fErr=""                                       # N . <-средн.--> <-СИ балл--> <-дисп.-->
    print sprintf("%4s %2s.%-2s %2s.%-2s %2s.%-2s",aj[3],aj[4],aj[5],aj[1],aj[2],aj[6],aj[7]), gFil
    exit
  }
}

END{
  if(fErr!=""){
    print fErr,fNam                               # у нас проблема...
    exit 1
  }
}

    Имя файла скрипта: "fHtml.awk" V-текст скрипта-V
- - - - - - - - -
BEGIN{                                      #  fHtml.awk - оформление в HTML рейтинга "siTopTexts"
  if(ARGV[1]=="")exit                             # защита от случайного пуска

# 2023.07.29, nvv, мелкая косметика
# ! прочая история - в подвале

  rTyp="<b>si&#x54;op&#x54;exts</b>"              # siTopTexts
  ancW="t"                                # общий признак типа рейтинга в якорях, комментах и т.п.
  ancS=":"                                  # частный признак якоря места (в формате: "ггммвв:NNN"
  nAnc="nvv"                                      # спец.якорь для первой МОЕЙ вещи ;)
  flag=0                                          # флаг для чередующейся раскраски строк таблиц
  tGap=100                                        # строк в одной таблице
  tMax=0                                          # строк в списке всего
  maxH=0                                          # максимальные хиты текста (1-й в рейтингше)
  totH=0                                          # сумма хитов всех текстов
  nAv1=0                                          # новый автор
  oldT=0                                          # размещено более года назад
  curT=0                                          # размещено в границах обсчитываемого интервала
  ficT=0                                          # фикция (за верхней границы интервала)
  nTx1=0                                          # вверх
  nTx2=0                                          # новинки
  nTx3=0                                          # вниз
  nSz1=0                                          # объём+
  nSz2=0                                          # объём-
  nUs1=0                                          # успех+
  nUs2=0                                          # успех-
  tNot=0                                          # число мошенников с ограниченным доступом
  mNot="<b>~ ? ~</b>"
  updH=0                                          # поправленные хиты за "30 дн."
  dOld=""
  hDat=-1                                         # 
  donH=-1
  hPag=-1
  nHit=-1
  hitL=-1
  hMax=-1
  nulG=-1
  badG=-1
  rNot=-1
  zipR=-1
# "/" == общий префикс урла СИ: samlib.ru
  nUrl="n/nosow_w_w"                              # nvv-с...
#  jDat=strftime("%y%m%d%H%M%S",systime())         # дата для якорей таблиц

  for ( jj in ENVIRON ) {                     # ! регистр букв в имени переменной может быть любой
    j1=tolower(jj)
    if ( j1 == "fold" ) fOld=ENVIRON[jj]          # ИМЯ опорного файла (like 2018.11.29.mText)
    if ( j1 == "hdir" ) hDir=ENVIRON[jj]"\\"      #  место для файлов *.HTML
  }
  if(fOld=="")fOld="U:\\Stor\\Text\\zOld.dTexts\\2021.05.26.dText"

  nMth[ 1]="января"
  nMth[ 2]="февраля"
  nMth[ 3]="марта"
  nMth[ 4]="апреля"
  nMth[ 5]="мая"
  nMth[ 6]="июня"
  nMth[ 7]="июля"
  nMth[ 8]="августа"
  nMth[ 9]="сентября"
  nMth[10]="октября"
  nMth[11]="ноября"
  nMth[12]="декабря"

   color[0]="\"#FFFFE0\""                     # (четная строка таблицы) светло-жёлтый line color 0
   color[1]="\"#F5F5DC\""                   # (НЕчетная строка таблицы) грязно-жёлтый line color 1
   color[2]="\"#C0C0C0\""                         # gray серый (всё плохо)
   color[3]="\"#FFF4E0\""                         # светло-розовый (ниже, меньше, плохо, нет)
   color[4]="\"#F0FFE0\""                         # светло-зелёный (выше, больше, хорошо)
   color[5]="\"#FFFFB0\""                         # светло-жёлтый - новое
   color[6]="\"#FFFF70\""                       # yellow желтый (нечётные ячейки в шапках таблиц)
   color[7]="\"#F8F8F8\""                         # светло-серый - наш рейтинг
   color[8]="\"#0000FF\""                         # синий (птичка nvv)
   color[9]="\"#000000\""                         # чёрный (вратый объём)
  color[10]="\"#E8F8B8\""                         # зелёный - ячейки с текстом нашего рейтинга
  color[11]="\"#E0F8FF\""                 # светло-сиреневый - размещено "давно" больше года назад
  color[12]="\"#FFD700\""                         #  оранжевый "потеряшки" /скрытые разделы/ aad2

  jr=split(ARGV[1],aq,"\\")                       # U:\Jobs\#qMonths\ +2020.06.03.vText
  dEnd=substr(aq[jr],1,10)
  fOut=hDir aq[jr] ".html"                        #  U:\Stor\Html\ +2020.06.08.dText +.html
  fPre=hDir aq[jr] ".z.html"                      #  U:\Stor\Html\ +2020.06.08.dText +.z.html
  rYea=substr(aq[jr],1,4)                         # год рейтинга !привязан к верхней дате выборки
  rMth=substr(aq[jr],6,2)                         # месяц его же
  rDay=substr(aq[jr],9,2)                         # день его же

  dBeg=strftime("%Y.%m.%d",mktime(rYea" "rMth" "rDay" 12 00 00")-86400*29) # дата 1-го дня выборки
  yBeg=substr(dBeg,1,4)                           # год этого 1-го дня интервала нашей выборки
  oldD=rYea-1"."rMth"."rDay                       # "год назад" - привязка к КОНЦУ нашей выборки
  tOld="года"
  qGap="30дн."
  info="<!-- (L) nvv; type: '30days' done: }"strftime("%Y.%m.%d %H:%M")"{ -->" # mark HTML
  inf2="<!-- (L) nvv; type: '30days' \"prev siTopTexts\" "strftime("%Y.%m.%d %H:%M")" -->"
  print "--- " strftime("%H:%M:%S") " Begs..."  # начали работу (оживляж;)

# † массив строк шапки общий
# # информационная вставка --> •¦
  if(strftime("%j")-355>0)jj="С наступающим"
  else if(strftime("%j")-11<0)jj="С"
  else jj=""
  if(jj!=""){
    aHnY[++nHnY]="<table align=center border=0 cellspacing=0 cellpadding=2>"
    aHnY[++nHnY]="<tr align=center bgcolor=navy><td><font size=+2 color=aqua>"
    aHnY[++nHnY]="<b>&nbsp;&#x2744; "jj" новым годом! <font color=white>&#x2744;"
    aHnY[++nHnY]="<font size=+2 color=lime>Happy New Year! &#x2744;&nbsp;</b></td>"
    aHnY[++nHnY]="</tr></table>"
    aHnY[++nHnY]="<hr>"
  }
#  aHdr[++nHdr]="<table align=center border=1 cellspacing=0 cellpadding=2>"
#  aHdr[++nHdr]="<tr align=center bgcolor=LightYellow><td><font size=+1><b>&nbsp;NB."
#  aHdr[++nHdr]="В <a href=/comment/"nUrl"/si_top500look-curr>гостевой</a>"
#  aHdr[++nHdr]="(реплика #58.) - <font color=brown>ОПРОС..." # <font color=brown>
#  aHdr[++nHdr]="</font>Мне интересно ваше мнение. :-!&nbsp;</b></td>"
#  aHdr[++nHdr]="</tr></table>"
#  aHdr[++nHdr]="<hr>"
# # <!-- информационная вставка (справка в шапке)
  aHdr[++nHdr]="См. <i>также</i>:"
  aHdr[++nHdr]="<br>?"
  aHdr[++nHdr]="<a href=/"nUrl"/si_pop-brief.shtml>Как узнать<b>"
  aHdr[++nHdr]="популярность произведения</b> в \"Самиздате\"</a>"
  aHdr[++nHdr]="(\"<b>где/что смотреть</b>\" - основное, вкратце)."

# † массив строк шапки ТОЛЬКО у последнего рейтинга
  aHdr[++nHdr]="<br>+"
  aHdr[++nHdr]="<a href=/"nUrl"/si_top6author-curr.shtml>"
  aHdr[++nHdr]="Рейтинг <b>siTopAuthors</b> \"Авторы\"</a>"
  aHdr[++nHdr]="(\"Самиздат\" <b>намедни</b>, приложение к "rTyp")."
  aHd1[++nHd1]="<br>#"
  aHd1[++nHd1]="<a href=/"nUrl"/sitopscriptslist.shtml>"
  aHd1[++nHd1]="Исходные <b>тексты скриптов</b> задач \"<b>siTop…</b>\"</a>"
#   aHd1[++nHd1]="<br>="
#   aHd1[++nHd1]="<a href=/"nUrl"/si_top100repls-current.shtml>"
#   aHd1[++nHd1]="Сборка "rTyp"</a> (среда,"
#   aHd1[++nHd1]="<i>скрипты</i>, обстоятельства и рекомендации)."
  aHd1[++nHd1]="<br>*"
  aHd1[++nHd1]="<a href=/"nUrl"/si_top-old_ones.shtml>История-<b>Р</b>,"
  aHd1[++nHd1]="\"<i>прикопанное</i>\" <b>прошлое</b></a>"
  aHd1[++nHd1]="(ссылки на подборки старых рейтингов по полугодиям)."
  aHd1[++nHd1]="<br>&#x00B1;" # <font color=red size=-2>New</font>"
  aHd1[++nHd1]="<a href=/n/nosow_w_w/si_votes-brief.shtml><b>Оценка произведений</b>"
  aHd1[++nHd1]="в \"Самиздате\"</a> (<b>что, где, как</b> и почему&hellip;)."
  aHd1[++nHd1]="<br>&#x221A;"
  aHd1[++nHd1]="<a href=/n/nosow_w_w/si-ini_reg.shtml><b>Регистрация</b> в \"Самиздате\" для получения"
  aHd1[++nHd1]="статуса &#171;<b>пользователь</b>&#187;</a> (оформление своего аккаунта)."
  aHd1[++nHd1]="<hr>"

  aHd1[++nHd1]="<b>NB.</b> <i>Названия</i> произведений и <i>псевдонимы</i>"
  aHd1[++nHd1]="авторов (как и прочие <i>сведения</i> в таблицах) приведены"
  aHd1[++nHd1]="<i>\"<a title=\"как есть\""
  aHd1[++nHd1]="href=\"https://ru.wikipedia.org/wiki/%D0%9A%D0%B0%D0%BA_%D0%B5%D1%81%D1%82%D1%8C\"><u>as"
  aHd1[++nHd1]="is</u></a>\", по состоянию на момент</i> сбора"
  aHd1[++nHd1]="данных. <i>Текущее</i> состояние можно узнать, пройдя"
  aHd1[++nHd1]="<i>по ссылкам</i> (в ячейках таблиц)."
  aHd1[++nHd1]="<br><b>(!)</b> <i><u>Если</u></i> на месте некоторых букв"
  aHd1[++nHd1]="(символов) в <i>названии</i> произведения или в <i>псевдониме</i>"
  aHd1[++nHd1]="автора вы видите \"<i><u>пустоту</u></i>\", то <i><u>у"
  aHd1[++nHd1]="вас</u></i>, скорей всего, <i><u>отсутствует</u></i>"
  aHd1[++nHd1]="шрифт с такими символами (иероглифы, руны, etc.)."
  aHd1[++nHd1]="<br><b>(#)</b>"
#   aHd1[++nHd1]="<font color=\"#DC143C\">NEW</font>" #  2023.06.23 crimson
  aHd1[++nHd1]="<font color=\"#A52A2A\"><b>Скрытые</b></font> авторы <u><i>отсутствуют</i></u>" # brown
  aHd1[++nHd1]="в \"<a href=/rating/hits/>Рейтинге по количеству посетителей</a>\""
}

function dat_str(d0,d2) {       # "2020.05.23" -> "23 мая" (d2==nul) or "23 мая 2020 г." (d2!=nul)
  d1=split(d0,ad,".")
  if(!d2)return("<b>"ad[3]+0" "nMth[ad[2]+0]"</b>")
  else   return("<b>"ad[3]+0" "nMth[ad[2]+0]"</b> "ad[1]" г.")
}

function mkPage() {               # сборка всей страницы рейтинга/приложения (тип, строк, куда-с)

# † массив строк шапки краткой справки последнего рейтинга
  aTb1[++nTb1]="<hr>"
  aTb1[++nTb1]=""
  aTb1[++nTb1]="<table align=center border=1 cellspacing=0 cellpadding=2>"
  aTb1[++nTb1]="<tr align=center>"
  aTb1[++nTb1]="<td bgcolor="color[1]" colspan=\"14\">Краткая сводка + цвет некоторых"
  aTb1[++nTb1]="ячеек таблиц (<i>предыдущий рейтинг -"
  aTb1[++nTb1]="<a href=/"nUrl"/si_top500look-prev.shtml><b>"dOld"</b></a></i>)</td></tr>"

# † массив строк шапки краткой справки ПРЕДпоследнего рейтинга
  aTb2[++nTb2]="<hr>"
  aTb2[++nTb2]=""
  aTb2[++nTb2]="<table align=center border=1 cellspacing=0 cellpadding=2>"
  aTb2[++nTb2]="<tr align=center>"
  aTb2[++nTb2]="<td bgcolor=\"#F5F5DC\" colspan=\"14\">Краткая сводка + цвет некоторых"
  aTb2[++nTb2]="ячеек таблиц (<i>предыдущий рейтинг - "dOld"</i>, последующий"
  aTb2[++nTb2]="- <a href=/"nUrl"/si_top500look-curr.shtml><b>здесь</b></a>)</td></tr>"

# † массив строк тела краткой справки общий
  aTbl[++nTbl]="<tr align=center><td bgcolor="color[0]" colspan=\"1\"><b>N</b>,"
  aTbl[++nTbl]="сдвиг места</td></td>"
  aTbl[++nTbl]="<td rowspan=\"4\"></td>"
  aTbl[++nTbl]="<td bgcolor="color[0]"><b>Произведения</b></td>"
  aTbl[++nTbl]="<td rowspan=\"4\"></td>"
  aTbl[++nTbl]="<td bgcolor="color[0]"><b>Авторы</b></td>"
  aTbl[++nTbl]="<td rowspan=\"4\"></td>"
  aTbl[++nTbl]="<td bgcolor="color[0]"><b>Размещено</b></td>"
  aTbl[++nTbl]="<td rowspan=\"4\"></td>"
  aTbl[++nTbl]="<td bgcolor="color[0]"><b>Объём</b> стал</td>"
  aTbl[++nTbl]="<td rowspan=\"4\"></td>"
  aTbl[++nTbl]="<td bgcolor="color[0]"><b>Оценки</b></td>"
  aTbl[++nTbl]="<td rowspan=\"4\"></td>"
  aTbl[++nTbl]="<td bgcolor="color[0]"><b>Отклик</b></td>"
  aTbl[++nTbl]="</tr><tr align=center>"
  aTbl[++nTbl]="<td bgcolor="color[4]">вверх.<b>" nTx1"</b></td>"
  aTbl[++nTbl]="<td bgcolor="color[5]"rowspan=\"2\">новые.<b>"newN"</b></td>"
#   aTbl[++nTbl]="<td bgcolor="color[5]">новые.<b>"newN"</b></td>"
#   aTbl[++nTbl]="<td bgcolor="color[5]"rowspan=\"2\">новые.<b>"nAv1"</b></td>"
  aTbl[++nTbl]="<td bgcolor="color[5]">новые.<b>"nAv1"</b></td>"
  aTbl[++nTbl]="<td bgcolor="color[4]">"qGap"<b>"curT"</b></td>"
  aTbl[++nTbl]="<td bgcolor="color[4]">больше.<b>"nSz1"</b></td>"
  aTbl[++nTbl]="<td bgcolor="color[4]">хорошие.<b>"nUs1"</b></td>"
  aTbl[++nTbl]="<td bgcolor="color[3]"rowspan=\"2\">нет.<b>"rNot"</b></td>"
  aTbl[++nTbl]="</tr><tr align=center>"
  aTbl[++nTbl]="<td bgcolor="color[3]">вниз.<b>" nTx3"</b></td>"
#   aTbl[++nTbl]="<td bgcolor="color[12]">скрытые.<b>"newN"</b></td>"
  aTbl[++nTbl]="<td bgcolor="color[12]">скрытые.<b>"length(a90)"</b></td>"
  aTbl[++nTbl]="<td bgcolor="color[11]">давно.<b>"oldT"</b></td>"
  aTbl[++nTbl]="<td bgcolor="color[3]">меньше.<b>"nSz2"</b></td>"
  aTbl[++nTbl]="<td bgcolor="color[3]">плохие.<b>"nUs2"</b></td>"
  if(tNot){
    aTbl[++nTbl]="</tr><tr align=center>"
    aTbl[++nTbl]="<td></td><td></td><td></td><td></td>"
    aTbl[++nTbl]="<td title=\"Доступ ограничен\" bgcolor="color[9]\
"><font color="color[6]">"mNot" "tNot"</font></td>"
  }
  aTbl[++nTbl]="</tr></table>"
  aTbl[++nTbl]="<table align=center border=0 cellspacing=0 cellpadding=2><tr>"
  aTbl[++nTbl]="<td>(</td>"
  aTbl[++nTbl]="<td bgcolor="color[11]"><b><i>давно</i></b> –"
  aTbl[++nTbl]="больше <b>"tOld"</b> назад</td>"
  aTbl[++nTbl]="<td>)</td>"
  aTbl[++nTbl]="</tr></table>"
#  aTbl[++nTbl]="<table align=center border=0 cellspacing=0 cellpadding=2><tr>"
#  aTbl[++nTbl]="<td bgcolor="color[4]"><b>NB.</b> Хиты"
#  aTbl[++nTbl]="произведений, опубликованных <b>после</b>"
#  aTbl[++nTbl]=dat_str(dBeg)", учтены <u>по факту</u>"
#  aTbl[++nTbl]="(„<b>¦</b>“ - с даты размещения)"
#  aTbl[++nTbl]="</tr></table>"
  aTbl[++nTbl]="<hr>"
  aTbl[++nTbl]=""
  aTbl[++nTbl]="<font size=\"-2\" face=monospace color="color[8]">"
  aTbl[++nTbl]="&nbsp;&nbsp;/\\&nbsp;&nbsp;&nbsp;&nbsp;/\\&nbsp;&nbsp;<br>"
  aTbl[++nTbl]="&nbsp;·--·--·--·&nbsp;<br>"
  aTbl[++nTbl]="&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\\/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>"
  aTbl[++nTbl]="&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/\\&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>"
  aTbl[++nTbl]="&nbsp;&nbsp;&nbsp;&nbsp;·--·&nbsp;&nbsp;&nbsp;&nbsp;<br></font>"

# † титулы (названия) и рейтинга и его копии "Предыдущий"
  Ttl1="<center><font size=\"+2\">Рейтинг "rTyp"-"ancD"</font>"
  Ttl2="<center><font size=\"+2\">Предпоследний рейтинг "rTyp"-"ancD"</font>"

# † массив строк тела рейтинга (всё оставшееся кроме последней строки)
  aOut[++nOut]="<br>\"Самиздат\", самые популярные произведения"
  aOut[++nOut]="<table align=center border=0 cellspacing=0 cellpadding=2>"
  aOut[++nOut]="<tr bgcolor="color[4]">"
  j0="с "dat_str(dBeg)
  if(yBeg!=rYea)j0=j0" "yBeg" г."
  j0=j0" по "dat_str(dEnd,1)"&nbsp;"
  aOut[++nOut]="<td>&nbsp;за 30 дней: "j0"</td>"
#  if(dOff!="0.0")aOut[++nOut]="<td bgcolor="color[3]">(<i><u>нет данных</u> за: "dOff"</i>)</td>"
  aOut[++nOut]="</tr></table></center></blockquote>"
  bLin=mkCap()                                    # шапка - первая (нулевая-с) строка таблиц
  lMax=tMax                                       # всего строк в таблице
  for ( tIni=1; tIni<=lMax; tIni=tIni+tGap ) {
    aOut[++nOut]="<blockquote>"  # начало очередной таблицы
    aOut[++nOut]="<table align=center border=1 cellspacing=0 cellpadding=2>"  # навигация в
    aOut[++nOut]="<caption><a id=\""ancD ancW tIni"\"></a>"mkGoTo(tIni)"</caption>"  # заголовке
    aOut[++nOut]=bLin  # шапка таблицы
    tEnd=tIni+tGap
    for (tBeg=tIni;tBeg<tEnd;tBeg++) {
      if(tBeg<=lMax) aOut[++nOut]=mkTabLn(tBeg,ordr[tBeg])  # строка данных в таблице
    }
    if ((tBeg-tMax)>=0) aOut[++nOut]=bLin
    aOut[++nOut]="</table>"  # таблица готова
    aOut[++nOut]="</blockquote>"  # закрытие блока
  }
  aOut[++nOut]="<blockquote>"  # подвал страницы
  aOut[++nOut]="<center>" mkGoTo(0) "</center>"  # заголовке
  aOut[++nOut]="<ul>"
  aOut[++nOut]="<table border=0 cellspacing=0 cellpadding=2><tr>"  # навигация в
  aOut[++nOut]="<td><b>Попутные сведения</b> (к "rTyp"-"ancD")"
  aOut[++nOut]="<td></tr></table>"
  aOut[++nOut]="<li>Справочник ИСС-СИ"
  aOut[++nOut]="\"<a href=/rating/hits/index.shtml><i><b>Рейтинг по количеству"
  aOut[++nOut]="посетителей</b></i></a>\" за <b>"hDat"</b>."
  aOut[++nOut]="<br>(от "dat_str(donH)", всего страниц: "hPag")"
  aOut[++nOut]="<br>Интервал хитов (<u>за 12мес</u>) у отобранных разделов:"
  aOut[++nOut]="<ul>"
  aOut[++nOut]="<li><i>Лидер</i> (1-е место): <b>"hMax"</b>."
  aOut[++nOut]="<li><i>Замыкающий</i> ("nHit"-е): <b>"hitL"</b>."
  aOut[++nOut]="</ul>"
  aOut[++nOut]="<li><i>Прочие</i> данные - по состоянию на "dat_str(hold)"."
  if(updH)aOut[++nOut]="<li>Произведения с хитами <i>по факту</i> „<b>¦</b>“: <b>"updH"</b>."
  j0=0
  for(j1 in a90)j0=j0+a90[j1]
  if(j0)aOut[++nOut]="<li>Произведения из „<i>скрытых</i>“ разделов: <b>"j0"</b>."
  aOut[++nOut]="<li>Среди гостевых (у <b>"tMax"</b> произведений списка):"
  aOut[++nOut]="<ul>"
  if(nulG)aOut[++nOut]="<li><i>недоступные</i>: <b>"nulG"</b>;"
  if(badG)aOut[++nOut]="<li><i>повреждёные</i>: <b>"badG"</b>;"
  if(rNot)aOut[++nOut]="<li><i>пустые</i> (кулуары•\"<i>антимат</i>\"•без реплик): <b>"rNot"</b>;"
  if(zipR)aOut[++nOut]="<li><i>с архивами</i>: <b>"zipR"</b>."
  aOut[++nOut]="</ul>"
  if(tNot){
    aOut[++nOut]="<br><table border=0 cellspacing=0 cellpadding=2><tr bgcolor="color[9]">"
    aOut[++nOut]="<td><font color="color[6]">&nbsp;Произведения с <b>ограниченным</b>"
    aOut[++nOut]="("mNot") <i>доступом</i>: <b>"tNot"</b>.&nbsp;</td>"
    aOut[++nOut]="</tr></table>"
  }
  aOut[++nOut]="</ul>"
  aOut[++nOut]="<table align=center border=0 cellspacing=0 cellpadding=2>"  # навигация в
  aOut[++nOut]="<tr><td>(copyleft "rYea"</td>"
  aOut[++nOut]="<td bgcolor="color[6]"><b>/\\/.\\/.\\/.</b></td><td>)</td></tr></table>"
}

function mkCap() {        # сборка шапки соответствующего типа таблиц (птичка или ссылка в ячейке)
  tmp0= "<tr align=center bgcolor="color[flag]">" # шапки начало; v-место:хиты
  tmp0=tmp0 "<td title=\"Место:сдвиг в списке, Хиты:за "qGap"\" bgcolor="color[6]"><b>N</b>: Хиты</td>"
  tmp0=tmp0 "<td><b>Произведение</b></a></td>"    # Произведение
  tmp0=tmp0 "<td bgcolor="color[6]"><a title=\"Раздел за 12мес: хиты, место\"><b>Автор(ы)</b></a></td>" # Автор
  tmp0=tmp0 "<td><a title=\"Дата создания текста + дата его последнего изменения\"" # Создан +v
  tmp0=tmp0 "><small>Размещено *<b>Изменено</b></small></a></td>" # ^+ Создан
  tmp0=tmp0 "<td bgcolor="color[6]"><a title=\"Текст в килобайтах\""  # Объём +v
  tmp0=tmp0 "><small><b>Объём</b></small></a></td>" # ^+ Объём
  tmp0=tmp0 "<td><a title=\"Текста\"><small><b>Форма</b>: Жанр(ы)</small></a></td>" # Форма: Жанр(ы)
  tmp0=tmp0 "<td bgcolor="color[6]"><a title=\"Уровень=(Оценки-5.5)*ln(голоса)\"" # Оценки +v
  tmp0=tmp0 "><small><b>Оценка</b></small></a></td>"  # ^+ Оценки
  tmp0=tmp0 "<td><a title=\"Реплик в сутки\""     # Отклик +v
  tmp0=tmp0 "><small><b>Отклик</b></small></a></td>"  # ^+ Отклик
  return( tmp0 "</tr>" )                          # шапка готова
}

function mkTabLn(nLn,tLn) { # таблица: сборка строки номер nLn (строка siTopTexts за номером tLn)
  flag=!flag                                      # чередующаяся раскраска строк
  hLin="<tr align=center bgcolor="color[flag]">"  # собираем очередную строку...
  hLin=hLin"<td "a00[tLn]" title=\""a02[tLn]"\"><a id=\""ancD ancS nLn"\"></a><b>"nLn"</b>: "  # <- место:хиты -v
  if(a23[tLn]!="")hLin=hLin"<a title=\"Хиты по факту.\""
  else            hLin=hLin"<a title=\"Хиты за "qGap"\""
  hLin=hLin "href=/"a10[tLn]"/stat.shtml#"a20[tLn]".shtml>"a22[tLn] a23[tLn]"</a></td>" # <-место:хиты-^
  hLin=hLin "<td align=left"                      # <- text -v
  if(a10[tLn]==nUrl) hLin=hLin " bgcolor=" color[10]  # nvv line ;)
  else if(a00[tLn]==" bgcolor="color[5])hLin=hLin " bgcolor="color[5] # new text
  hLin=hLin "><a href=/" a10[tLn] "/" a20[tLn] ".shtml id=\"" ancA[tLn] ancS a20[tLn] # <- text -^
  hLin=hLin "\">" a21[tLn] "</a></td><td align=left"  # ^- text -> <- автор-v
  if(a10[tLn]==nUrl) hLin=hLin " bgcolor=" color[10]  # nvv line ;)
  else{
    if(a12[tLn]) hLin=hLin a77[tLn] # у автора/раздела ЕСТЬ место в рейтинге "по хитам" 2023.06.24
    else hLin=hLin " bgcolor=" color[12]
  }
  hLin=hLin "><a href=/"a10[tLn]"/ id=\"" ancA[tLn] "\" title=\"Хиты:"a13[tLn]
  if(a12[tLn]) hLin=hLin ", место:"a12[tLn]a78[tLn]"\">" a11[tLn] "</a></td>" # <- автор -^
  else         hLin=hLin ", скрытый\">" a11[tLn] "</a></td>" # <- автор -^
  hLin=hLin "<td "a34[tLn]"><small>"a30[tLn]" *<b>"a31[tLn]"</b></small></td>" # изменено/размещено (текст)
  if(a33[tLn]) hLin=hLin "<td align=right"a81[tLn]">" a82[tLn] a32[tLn] "k"a83[tLn]"</td>" # <- объём
  else hLin=hLin "<td align=right title=\"Доступ ограничен\" bgcolor="color[9]"><font color="\
color[6]">"a32[tLn] "</font></td>"               # <- объём, "доступ ограничен" -^
  hLin=hLin "<td><small>" a40[tLn] "</small></td>"  # жанр(ы)
  if(a50[tLn]==0)hLin=hLin "<td><small>--</small></td>" # оценки+успех +v
  else{
    hLin=hLin "<td"a52[tLn]"><a title=\"Среднее » баллыСИ *шт. ==Уровень: "a51[tLn]
    hLin=hLin "\" href=/cgi-bin/vote_show?DIR="a10[tLn]"&FILE="a20[tLn]">"
    hLin=hLin "<small>"a50[tLn]"</small></a></td>"  # ^+ оценки+успех
  }
  if(a62[tLn]==-1)hLin=hLin"<td bgcolor="color[2]"><small>-X-</small></td>" # <- недоступно-с
  else if(a62[tLn]==0){                           # <- гостевая пуста
    hLin=hLin "<td bgcolor="color[3]" title=\"Пусто или кулуары\"><small>"
    hLin=hLin "<a href=/comment/"a10[tLn]"/"a20[tLn]">:)(:</a></small></td>"
  }else{
    hLin=hLin "<td><small><a href=/comment/"a10[tLn]"/"a20[tLn] # отклик +v
    hLin=hLin " title=\"Реплик в сутки (шт./дни): "a63[tLn]"\">"a64[tLn]"</a></small></td>" # ^+ отклик
  }
  return (hLin "</tr>")                           # строку собрали, вываливаем!
}

function mkGoTo(lCur) {                           # сборка строки навигации для титула таблиц
  lBeg=1                                          # начало очередного интервала
  lEnd=lBeg+tGap-1                                # и его конец
  if ((lEnd-lMax)>0) lEnd=lMax                    # но не выше общего конца
  sNav="<center>Интервал <b>N</b>: <b>"           # начало строки навигации
  for(j0=1;j0<=lMax;j0++) {
    if (j0==lBeg) {                               # компоновка очередного элемента навигации
      if ((lBeg-1)>0) sNav=sNav " | "             # первый разделитель опускаем
      if (lBeg==lCur) sNav=sNav "<big>" lBeg "-" lEnd "</big>"  # это у своей таблицы, v-иначе
      else {
        sNav=sNav "<a title=\"Перейти к интервалу: " lBeg "-" lEnd "\" href=\"#"
        sNav=sNav ancD ancW j0"\">" lBeg "-" lEnd "</a>" # оформление как ссылка на ЯКОРЬ
      }
      lBeg=lEnd+1                                 # сдвинем границы на шаг tGap
      lEnd=lEnd+tGap
      if ((lEnd-lMax)>0) lEnd=lMax
    }
  }
  sNav=sNav"</b></center>"                        # завершение строки
  return(sNav)
}

# hitT numP hitP sizT  aNum rNum   begD      endD       aDat     "namA" "namT" id_P id_T votT jnrT down gFil
# $1    $2   $3   $4    $5   $6     $7        $8         $9       $10    $11   $12  $13  $14  $15  $16  $17
# 24659 12 332071 275.1 -478 478 2018.07.01 2018.11.11 2018.07.18 "Федорочев•Алексей" "Лось" f/fedorochew_a 2018 "6.89*760" 0 v f-fedorochew_a.2018
# 1966 116 59167 1821.1 -199 199 2014.07.10 2017.11.30 2018.02.23 "Скс." "Режим•бога" s/sks 111 0 0 : s-sks.111

{         # загрузка исходного списка (с разборкой и оформлением значений, отображаемых в ячейках)
  if(match($1,/^[0-9]+$/)){                       # признак - "строка основных данных"
    tMax++
    jj=NR-1                                       # 1-st line - hold date
    a00[jj]=0                       # пока неизвестная позиция текста в предыдущем bStat.20??.??.#
    a01[jj]=jj                                    # позиция текста в siTopTexts
    a02[$12"/"$13]=jj                             # обратная ссылка на позицию по урлу текста
    a10[jj]=$12                                   # автор, id_P - урл раздела (мой - n/nosow_w_w;)
    a11[jj]=unpack($10)                           # автор, namA - ФИО
    a12[jj]=$2      #  автор, numP - позиция раздела в рейтинге посещаемости "по зитам" 2023.06.24
    if(!a12[jj])a90[$12]++                    #  СКРЫТЫЙ раздел, учтём произведение из 2023.06.23
    a13[jj]=$3                              # автор, hitP - хиты раздела (в рейтинге посещаемости)
    a20[jj]=$13                                 # текст, id_T - имя файла (без расширения .shtml)
    ancA[jj]=substr($12,1,1)"~"substr($12,3)      # формат: "aChr~aNik"
    a21[jj]=unpack($11)                           # текст, namT - заголовок
    a22[jj]=$1                                  # текст, hitT - хиты текста (в статистике раздела)
    if(match($16,/^[0-9]+\]$/)){
      a23[jj]="<b>¦</b>"        # 16317.
      updH++
    }else a23[jj]=""
    a30[jj]=rusDat($7)                            # текст, begD - дата размещения в разделе
    a31[jj]=rusDat($8)                            # текст, endD - дата последнего изменения
    split($4,aq,".")
    a32[jj]=aq[1]                                 # текст, sizT - объём в килобайтах
    a33[jj]=aq[2]                       # текст, sizB - "1"==объёмOk, "0"=="доступ ... ограничен"
    if(!aq[2]){
      tNot++                                      # учтём и сосчитаем мошенника... :(
      a32[jj]=mNot                                # и покажем фигу, ВМЕСТО "размера"
    }
    a34[jj]=$7                                    # текст, создан в формате гггг.мм.дд
    a40[jj]=unquot(unpack($15))                   # текст, jnrT - жанр(ы) (может отсутствовать)
    a50[jj]=unpack($14)                           # текст, votT - оценки (могут отсутствовать)
    a60[jj]=$5            # гостевая, aNum номер первой реплики интервала (с минусом == в архиве)
    a61[jj]=$6                        # гостевая, rNum номер последней реплики интервала (текущая)
    a62[jj]=$9                                    # гостевая, aDat дата первой реплики интервала
    if(jj==1)maxH=$1                              # максимальные хиты текста (1-й в рейтингше)
    totH=totH+$1                                  # сумма хитов всех текстов
  }else{
    if($1 $2==";m""hold") {         # сводки сведения + проблемы, замеченные при сборе данных
# ;m hold 2019.07.29
      hold=$3                                     #  дата сборки "yyyy.MM.dd"
      ancD=substr(hold,3,2) substr(hold,6,2) substr(hold,9,2) #  дата сборки "yyMMdd"
    }else{
# ;1A hits 2:xNam 445:xNum 101285:xPas 2021.04.22:hDat 1018:hPag 806772:hMax 1018:DnLd 2021.04.27:done 25:mins gapT~14:53-15:18~ 
# ;2B stat 2021.04.27:sEnd    23:nPre      0:hPre    23:miss 300:minT 552:hitL 30072:nHit 806772:hMax last~00:26:00~
# ;2C scan 2021.04.24:dEnd 29987:nEnd  60688:hEnd 30010:nMig 1593:tNum 10:nGis 29174:nLow mod:v OkV:99.9% 
# ;4T text 0:nulG 0:badG 78:rNot 197:zipR 1479659:tHit 0:dnLd 2021.04.27:done 2:mins 16:07-16:09:gapT 
      if(match($0,/^;1A .+ 20..\.(.+)\.(.+):hDat /,aj))hDat=aj[2]"/"aj[1]  #  дата aHits по СИ-шному
      if(match($0,/^;1A .+ ([\.0-9]+):done /,aj))      donH=aj[1] #  дата "скачан" (aHits)
      if(match($0,/^;1A .+ ([0-9]+):hPag /,aj))        hPag=aj[1] #  страниц aHits
      if(match($0,/^;1A .+ ([0-9]+):hMax /,aj))        hMax=aj[1] #  хиты раздела-лидера aHits
      if(match($0,/^;2B .+ ([0-9]+):nHit /,aj))        nHit=aj[1] #  точное число топовых разделов
      if(match($0,/^;2B .+ ([0-9]+):hitL /,aj))        hitL=aj[1] #  хиты замыкающего за "12мес"
      if(match($0,/^;4T .+ ([0-9]+):nulG /,aj))        nulG=aj[1] #  гостевая недоступна
      if(match($0,/^;4T .+ ([0-9]+):badG /,aj))        badG=aj[1] #  гостевая повреждена
      if(match($0,/^;4T .+ ([0-9]+):rNot /,aj))        rNot=aj[1] #  гостевая пуста
      if(match($0,/^;4T .+ ([0-9]+):zipR /,aj))        zipR=aj[1] #  гостевая с арховом
    }
  }
}

function rusDat(jj) {                             # 2019.19.25 -> 25.19.2019
  split(jj,aq,".")
  return(aq[3]"."aq[2]"."aq[1])
}

function cutR(jj) {                               # из литерала типа "rNot=80"
  split(jj,aq,":")                                # выделяет правую часть
  return(aq[2])                                   # типа "80"
}

function unpack(j1) {               # восстановление литерала, свёрнутого при сборе сведений с СИ
  if (substr(j1,1,1)=="\"") j1=substr(j1,2,length(j1)-2)  # откусим обрамляющие двойные кавычки
  j2=""                                           # "•" -> пробел
  while(n=index(j1,"•")) { j2 = j2 substr( j1, 1, n-1 ) " "; j1 = substr( j1, n+1 ) }
  j1=j2 j1
  j2=""                                           # "”" -> двойные кавычки
  while(n=index(j1,"”")) { j2 = j2 substr( j1, 1, n-1 ) "&quot;"; j1 = substr( j1, n+1 ) }
  j1=j2 j1
  return( j1 )
}

function unquot(j1) {       # восстановление литерала "жанр(ы), свёрнутого при сборе сведений с СИ
  j2=""                                           # '&quot;' -> '"'
  while(n=index(j1,"&quot;")) { j2 = j2 substr( j1, 1, n-1 ) "\""; j1 = substr( j1, n+6 ) }
  j1=j2 j1
  return( j1 )
}

END{                            # данные загружены и разобраны, оформляем рейтинг и приложения...
  if(ARGV[1]==""){
    print "NO input file!"                        # защита от случайного пуска
    exit -1
  }
  if(hold=="") {
    print "NO date to bind guesbooks (no ';m' in 1-st line)"
    exit -1
  }
  if(hDat==-1)aVar["hDat"]=1                      #  дата aHits по СИ-шному
  if(donH==-1)aVar["donH"]=1                      #  дата "скачан" (aHits)
  if(hPag==-1)aVar["hPag"]=1                      #  страниц aHits
  if(nHit==-1)aVar["nHit"]=1                      #  точное число топовых разделов
  if(hitL==-1)aVar["hitL"]=1                      #  хиты замыкающего за "12мес"
  if(hMax==-1)aVar["hMax"]=1                      #  хиты раздела-лидера aHits
  if(nulG==-1)aVar["nulG"]=1                      #  гостевая недоступна
  if(badG==-1)aVar["badG"]=1                      #  гостевая повреждена
  if(rNot==-1)aVar["rNot"]=1                      #  гостевая пуста
  if(zipR==-1)aVar["zipR"]=1                      #  гостевая с арховом
  if(j0=length(aVar)){
    tmp0="? no var(s):"
    for(j1 in aVar)tmp0=tmp0" "j1
    print(tmp0" in "FILENAME)
    exit -1
  }
  endS=mktime(substr(hold,1,4)" "substr(hold,6,2)" "substr(hold,9,2)" 00 00 00")  # seconds "до"

# подвязка позиций прошлого рейтинга, для отметок "сдвиг" (места)
# hitT numP hitP sizT  aNum rNum   begD      endD       aDat     "namA" "namT" id_P id_T votT jnrT gFil
# $1    $2   $3   $4    $5   $6     $7        $8         $9       $10    $11   $12  $13  $14  $15  $16
# 6093 156 27017 1821.1 -291 291 2014.07.10 2017.11.30 2018.03.24 "Скс." "Режим•бога" s/sks 111 0 0 s~sks.111

  tmp0=0
  while ((getline line < fOld) > 0) {             # fOld: like 2018.11.29.mText
    split(line,aj)
#  ;m hold 2019.12.02 первая строка
    if(!tmp0)dOld= substr(aj[3],3,2) substr(aj[3],6,2) substr(aj[3],9,2) #"191202" (ID старого рейтинга)
    else if(match(aj[1],/^[0-9]+$/)){             # это именно строка с хитами текста &Co
      a76[aj[12]]++                               # старые авторы
      j0=aj[12]"/"aj[13]                          # полный ID старого текста
      a80[j0]=aj[4]
#      split(aj[4],aq,".")                         # объём 50.1 =ok, 50.0 =fake
#      if(aq[2]) a80[j0]=aq[1]                     # реальный объём старого текста
#      else      a80[j0]=0                         # вратый-с
      if(!a00[a02[j0]])a00[a02[j0]]=tmp0    # если этот текст неоптичен запомним, текст был здесь:
    }
    tmp0++                                      # старая позиция текста в старом списке
  }
  close(fOld)                                     # всё/не_всё, - старый список тогось
#   while(hitL-a22[tMax]>0)tMax--           # реальное число строк (текстов) с хитами НЕ менее hitL
  print("tMax:"tMax)
# siTopTexts - оформление заготовок строк (для основного рейтинга)
  split("", ordr)                                 # очистим массив
  newN=0
  for (jj=1;jj<=tMax;jj++){                 # порядок строк (основной цикл построчной подготовки)
    ordr[jj]=jj                                   # - как в источнике
    if(!a00[jj]){                                 # признак "новый текст в рейтинге"
      newN++                                      # и сосчитаем
    }
    if(!(a10[jj] in a76)){                        # новый автор
      a77[jj]=" bgcolor="color[5]                 # новый автор
      a78[jj]=" (Новый)"
      if(!(a10[jj] in a79)){                      # новый автор, первое вхождение
        a79[a10[jj]]++                            # запомним этого нового автора
        nAv1++                                    # и сосчитаем его же-с
      }
    }
# NN - место_в_рейтинге
    if(dEnd<a34[jj]){
      a00[jj]=" bgcolor="color[2]                 # место - "фикция" (до нашего интервала)
      a02[jj]="Фикция"
    }else{
      if(a00[jj]){                                # место в старом рейтинге нашлось?
        j0=a00[jj]-a01[jj]
        if(!j0){                                  # нулевой сдвиг
          a00[jj]=""                              # default color - основной цвет строки
          a02[jj]="Сдвиг:0=="
        }else if(j0>0){                           # "сдвиг" вверх "N-^"
          a00[jj]=" bgcolor="color[4]             # "сдвиг" вверх "N-^"
          a02[jj]="Сдвиг:"j0"-^"
          nTx1++                                  # вверх
        }else{                                    # "сдвиг" вниз "M-v"
          j0=0-j0
          a00[jj]=" bgcolor="color[3]             # "сдвиг" вниз "M-v"
          a02[jj]="Сдвиг:"j0"-v"
          nTx3++                                  # вниз
        }
      }else{                                      # новое "NEW" - светло-желтый
        a02[jj]="Новое"                           # подсказка в этой ячейке
        a00[jj]=" bgcolor="color[5]               # новое "NEW"
        nTx2++                                    # новинки
      }
    }
# изменение размера текста
    if(a33[jj]){                        # текст, sizB - "1"==объёмOk, "0"=="доступ ... ограничен"
      j0=a10[jj]"/"a20[jj]                        # id_P/id_T
      if(split(a80[j0],aq,".")==2){               # в старом рейтинге текст есть
        if(!aq[2])a80[j0]=0                       # "0"=="доступ ... ограничен" (враньё-с)
        else      a80[j0]=aq[1]                   # "1"==объёмOk
        j0=a32[jj]-a80[j0]
        if(!j0) a83[jj]="</a>"
        if(j0>0){
          a81[jj]=" bgcolor="color[4]             # стало больше
          a82[jj]="<a title=\"+"j0"k\">"          # стало больше
          nSz1++
        }
        if(j0<0){
#          j0=-j0
          a81[jj]=" bgcolor="color[3]             # стало меньше
          a82[jj]="<a title=\""j0"k\">"           # стало меньше
          nSz2++
        }
      }
    }
# Жанр(ы)
    split(a40[jj],aq,":")                         # разделим форму и жано(ы)
    if(aq[2]=="") aq[2]="--"                     # два дефиса подряд смотрятся красивше (jnrT;)
    a40[jj]="<b>"aq[1]"</b>: "aq[2]
# Оценки текста (7.25*15) пляски с бубном...
    if(a50[jj]!=0){
      split(a50[jj],aj,"*")
      j1=split(aj[1],az,"»")                      # az[1] - среднее, az[2] - SI баллы
      a50[jj]=az[1]"»<b>"az[2]"</b> *"aj[2]
      if(j1==1)j1=aj[1]
      else     j1=az[1]
      a51[jj]=sprintf("%3.2f",(j1-5.5)*log(aj[2]))  # уровень оценок
      if((a51[jj]+0)==0){
        a51[jj]=0                                 # ни рыба, ни мясо
        a52[jj]=""
      }else if(a51[jj]<0){
        a52[jj]=" bgcolor="color[3]               # плохие
        nUs2++                                    # успех-
      }else{
        a52[jj]=" bgcolor="color[4]               # хорошие
        nUs1++                                    # успех+
      }
    }
# Отклик в гостевых
    if(substr(a62[jj],5,1)=="."){                 # aDat (like 2018.07.18) считаем отклик
      j0=mktime(substr(a62[jj],1,4)" "substr(a62[jj],6,2)" "substr(a62[jj],9,2)" 00 00 00") # seconds "от"
      j0=(endS-j0+86400)/86400                    # days - интервал в днях
      j1=a60[jj]
      if(j1<0)j1=a61[jj]+j1+1000                  # nums - реплик в интервале
      else    j1=a61[jj]-j1+1
      a64[jj]=sprintf("%4.2f",j1/j0)              # reac - реплик в сутки (для сортировки)
      a63[jj]=j1"/"j0                             # сборка для вывода (Реплик в сутки (шт./дни))
      if(a60[jj]>0)a64[jj]=a64[jj]"•"             # архивов нет
    }
# выявим "свежак", влетевший в рейтинг менее чем за 30 дней a30 и "старьё"
    if(dEnd<a34[jj]){                             # до нашего интервала
      a34[jj]=" bgcolor="color[3]                 # фикция т.е. ПОСЛЕ нашего интервала
      ficT++                                      # счётчик "фикция"
    }else if(dBeg<=a34[jj]){                      # в нашем интервале до верхней границы
      a34[jj]=" bgcolor="color[4]
      curT++
    }else if(oldD>a34[jj]){                       # в нашем интервале до верхней границы
      a34[jj]=" bgcolor="color[11]
      oldT++                                      # счётчик "давно"
    }else  a34[jj]=""
  }
# собственно сборка файла с таблицами...
  if(500-tMax>=0)tGap=50
  if(250-tMax>=0)tGap=25
  mkPage()

  print("<blockquote>")                   >fOut
  print(info)                             >fOut   #  служебная отметка (для порядку)
  if(nHnY)for(jj=1;jj<=nHnY;jj++)print(aHnY[jj])  >fOut
  for(jj=1;jj<=nHdr;jj++)print(aHdr[jj])  >fOut
  for(jj=1;jj<=nHd1;jj++)print(aHd1[jj])  >fOut
  for(jj=1;jj<=nTb1;jj++)print(aTb1[jj])  >fOut
  for(jj=1;jj<=nTbl;jj++)print(aTbl[jj])  >fOut
  print(Ttl1)                             >fOut
  for(jj=1;jj<=nOut;jj++)print(aOut[jj])  >fOut
  print(info)                             >fOut   #  служебная отметка (для порядку)
  printf("</blockquote>")                 >fOut

  print("<blockquote>")                   >fPre
  print(inf2)                             >fPre
  if(nHnY)for(jj=1;jj<=nHnY;jj++)print(aHnY[jj])  >fPre
  for(jj=1;jj<=nHdr;jj++)print(aHdr[jj])  >fPre
  for(jj=1;jj<=nHd2;jj++)print(aHd2[jj])  >fPre
  for(jj=1;jj<=nTb2;jj++)print(aTb2[jj])  >fPre
  for(jj=1;jj<=nTbl;jj++)print(aTbl[jj])  >fPre
  print(Ttl2)                             >fPre
  for(jj=1;jj<=nOut;jj++)print(aOut[jj])  >fPre
  print(inf2)                             >fPre
  printf("</blockquote>")                 >fPre

  print "--- " strftime("%H:%M:%S") " Ends..."  # закончили работу (оживляж;)
# 2018.09.30, nvv creation
# ...
# 2020.05.09, nvv, +дополнения, + мелкая косметика
# 2020.05.21, nvv, раскраска по "Размещено" - "намедни", "недавно" и "давно"
# 2020.06.12, nvv, исправление замеченных неточностей + updates:
# 2020.06.27, nvv, anchors "place" added (теперь можно сослаться извне на любое место рейтинга)
# 2020.07.09, nvv, minor updates
#   = причёсана работа с датами границ потребных интервалов
#   = ведена поддержка "фикций" (хиты ДО размещения текста)
#   = немного обихожены даты и уточнено разграничение: "давно", "недавно", "фикция"
#   = немного изменена палитра раскраски ячеек
# 2020.08.21, nvv, подготовка к отказу от козырного числа 1000 в названии рейтинга
#   вместо "siTop1000texts" стало "siTopTexts"
# 2020.09.23, nvv, поддержка (ссылки на) предыдущего рейтинга
# 2020.09.28, nvv, число текстов в списке определяется по границе выборки bStat:
#   ;2D =Вс= hitL:20223.630 <- т.е. 20223 разделов с хитами "12мес" НЕ ниже 630
#   ergo -> в списке тексты с хитами "30дн." НЕ меньше 630.
# 2020.10.14, nvv, к жанрам текста добавлена форма (роман/повесть/справочник/etc.)
#     + исправлена мелкая ошибка в алгоритме + добавлен спец.якорь "nvvA" ;)
# 2020.10.31, nvv, мелкая косметика
# 2020.11.05, nvv, добавлены якоря ancA на авторов
#     и на сами произведения в формате: "aChr~aNik:id_T" ("ancT")
# 2020.11.09, nvv, добавлено отображение tNot числа текстов "для френдов"
#     (т.е. НЕ доступных "mNot" читателям) в списке
# 2020.11.18, nvv, мелкая косметика
# 2020.12.31, nvv, новогоднее поздравление... ;)
# 2021.01.02, nvv, мелочёвка
# 2021.01.09, nvv, мелочёвка
# 2021.02.12, nvv, мелочёвка
# 2021.03.20, nvv, мелочёвка
# 2021.04.24, nvv, переделки задачи bStat влекут переделки сопряжения
# 2021.05.10, nvv, теперь скрипт создаёт сразу и рейтинг (HTML) и копию в варианте "Предыдущий"
# 2021.05.23, nvv, исправлена устаревшая ссылка на "Историю-Р"
# 2021.06.09, nvv, мелкая косметика
# 2021.09.03, nvv, мелкая неточность (строка "по факту" ненужна, если таковых нет)
# 2021.11.22, nvv, разбиение на таблицы, макс число строк в одной (tGap) - зависит от общего оных числа
# 2021.12.26, nvv, автоматическая вставка поздравления с новым годом
# 2022.01.13, nvv, исправлена неточность в выявлении новогоднего периода
# 2022.10.19, nvv, в шапку добавлена ссылка на текст: "Регистрация в СИ""
# 2022.12.01, nvv, текст: "Регистрация в СИ" перестал быть New
# 2023.06.24, nvv, добавлена поддержка aad2 произведений из скрытых разделов
}

    Имя файла скрипта: "fHtml.cmd" V-текст скрипта-V
- - - - - - - - -
@if not defined nJob @echo off
echo %date% %time:~0,8% %~nx0 Begs
:: fHtml.cmd - оформление в HTML списка топовых текстов последних 30 дней
:: + 20??.??.??.dText (like 2018.12.30.dText) - оформляемый последний список
:: + 20??.??.??.dText (like 2018.11.29.dText) - опорный ПРЕД последний список

:: проверить-найти:
:: - свежий *.dText, например: 2021.04.27.dText
:: - старый *.dText, например: 2021.04.24.dText

:: - свежий *.dText, например: 2021.04.27.dText.html
:: - старый *.dText, например: 2021.04.24.z.dText.html


:: 2018.11.26, nvv, creation
:: ...
:: 2019.03.06, nvv, оформляемый месяц можно задать ЯВНО (вместо дефолтного)
:: 2019.07.31, nvv, мелкая косметика...
:: 2019.11.29, nvv, переменная nJob блокирует пилотную команду @echo off
:: 2019.12.06, nvv, current unification
:: 2020.03.21, nvv, some updates
:: 2021.05.29, nvv, очередная оптимизация с унификацией...
:: 2021.06.13, nvv, confirmation to move PREV data off
:: 2023.06.24, nvv, hidden authors debugging see: rem goto mkHtm
set nJob=fHtml
if NOT defined jDat set jDat=%date%
set ok2upDt=%1
set aHtm=%~dp0%nJob%.awk
::  wDir - общее рабочее место
set wDir=%~d0\Jobs
::  sDir - общее складское хранилище
set sDir=%~d0\Stor
::  dDir - склад для уже ненужных списков 20??.??.??.dText
set dDir=%sDir%\Text\zOld.dTexts
if NOT exist %dDir% set errInf=NO %dDir% found & goto nExit
::  hDir - склад под выходные файлы с таблицами *.html
set hDir=%sDir%\Html
set pDir=%hDir%\dTexts
if NOT exist %hDir%\ mkDir %hDir%
if errorLevel 1 set errInf=fail mkDir %hDir% & goto nExit

::  fOld - прошлый опорный список для "siTopTexts"
set fOld=
::  fCur - свежий список для "siTopTexts" - 1k популярных текстов (со всеми атрибутами)
set fCur=
for %%A in (%wDir%\20??.??.??.dText) do call :findCurOld %%A
if NOT defined fOld set errInf=!? NO old 20*.dText found & goto nExit
if defined errInf goto nExit

set fHtm=%hDir%\%dCur%.dText.html
echo prev:%fOld%
echo curr:%fCur%
echo fHtm:%fHtm%
if NOT exist %fHtm% goto mkHtm

:: итоговый файл есть. если нужно прибрать - идём прибирать, иначе выясняем...
if defined ok2upDt goto upDt

:: запрос (конечный HTML уже есть!)
set tmp0=
rem goto mkHtm
echo fHtm found; reBuild it? [yes] == y
set /p tmp0=?[no]?
if /i NOT %tmp0%. == y. (
  echo job canceled {NO to reBuild}
  exit
)
:mkHtm собственно оформлялка ("от и до";)
gawk.exe -f %aHtm% %fCur%
if errorLevel 1 set errInf=!!! X someThing's wrong with %~n0.awk & goto nExit
:: блокируется оптичивание преобразованного в HTML
goto nExit

:upDt  запрос (точно ненада?)
set tmp0=
echo confirm PREV data OFF? [yes] == y
set /p tmp0=?[no]?
if /i NOT %tmp0%. == y. (
  echo job canceled {NO to reBuild}
  exit
)
:: уберём уже ненужный старый список рейтинга на склад Text\zOld.dTexts
if exist %fOld% (
echo move %fOld% to %dDir%
move /Y %fOld% %dDir%
if errorLevel 1 set errInf=!! fail to move old *.dText to %dDir% & goto nExit
)
:: уберём опубликованное (в HTML)
set tmp0=%hDir%\%dCur%.dText.html
if exist %tmp0% (
echo move %tmp0% to %pDir%
move /Y %tmp0% %pDir%
if errorLevel 1 set errInf=!! fail to move %tmp0% to %pDir% & goto nExit
)
:: убьём опубликованное "предыдущее" (в HTML)
set tmp0=%hDir%\%dOld%.dText.z.html
if exist %tmp0% (
echo erase %tmp0%
erase %tmp0%
if errorLevel 1 set errInf=!! fail to erase %tmp0% & goto nExit
)

:nExit
if defined errInf (
  echo ! %errInf%
  set errInf=
)
echo %date% %time:~0,8% Job:%nJob% Ends
goto :eof

:findCurOld ::  dOld dCur - даты в именах файлов опорного и текущего списков
if defined fOld if defined fCur set errInf=too many *.dText files in %wDir% & goto :eof
set fOld=%fCur%
set dOld=%dCur%
set fCur=%1
set dCur=%~n1
goto :eof

    Имя файла скрипта: "gAuth.cmd" V-текст скрипта-V
- - - - - - - - -
@if not defined nJob @echo off
echo %date% %time:~0,8% %~nx0 Begs
:: gAuth.cmd - построение диф-списка (изменение хитов "12мес") за цикл обновления статистик
:: в _siTop.# ищутся два последних aHits, выявляются даты их расчётов (и интервал в днях;)
:: вычисляется среднесуточная посещаемость разделов (за последний интервал, по умочанию - 3 дня)
:: последовательность сборки рейтинга siTopAuthors "Авторы":

:: 2023.01.21, nvv, сменил разрешение экрана /меньше пикселей/, пришлось слегка менять оживляж
:: ! прочая история в подвале

:: нужно изменить порядок запросов при сборке списка годного
:: 1 статистика нет ни в #bStat, ни в #gAuth - скачать в #gAuth
:: 2 наличие -^- обеспечено - а текст с успехом "20+" есть? НЕТ == неинтересно
:: 3 скачиваем смотрим состояние текста /может быть больге одного - выбираем первый годный и учитываен кол-во/
:: 4 тексты ОК - тащим титул для сведений об "обновлялось"

:: основные сеты и прочая подготовка к работе...
set jDat=%date%
set nJob=gAuth
::  siUrl - URL Самиздата (будКлуб - глупость -- НЕ факт-с;)
set siUrl=samlib.ru
if exist \siUrl.budclub set siUrl=budclub.ru
::  dLjb - общий счётчик /формально/ успешно скачанного (НЕ 0b)
set dLjb=0
::  ratW - темп скачивания (секунд на один файл в среднем)
rem set ratW=0
::  wrks - общее время работы процесса (пуск общего таймера)
for /F %%A in ('gawk.exe "BEGIN{print(systime());exit}"') do set wrks=%%A
::  minL - минимальная удача раздела (число посетителей в сутки)*10 - граница отбора для последующей обработки
set minL=10000
:: . ^-30000 визитов за 30 дней v-300 оных же /визитов-с/ ;)
set minL=100
set errInf=
::  datJ - дата в имени последнего ВХОДНОГО списка bStat /"Популярные КНИГИ топовых авторов СИ"/
set datJ=
::  txtN - счётчик-флаг "можно выдавать итоговую строку в файл"
set txtN=0
::  txtM - счётчик "в разделе два или более текстов-лидеров"
set txtM=0
::  awkH - скрипт сравнения двух последних списков fHits
set awkH=%~p0%nJob%H.awk
::  awkI - скрипт разбора титула раздела (объём, обновлялось и проч.)
set awkI=%~p0%nJob%I.awk
::  awkS - скрипт поиска текста-лидера в статистике раздела
set awkS=%~p0%nJob%S.awk
::  awk_ - скрипт поиска текста-лидера в статистике раздела
set awk_=%~p0%nJob%_.awk
::  awkT - скрипт разбора и обустройства текста СИ
set awkT=%~p0dText.awk
:: - проверить комплектность AWK
for %%A in ( %awkH% %awkI% %awkS% %awkT% %awk_% ) do if NOT exist %%A (
  echo NO %%A
  exit
)
::  wGetSiz - размер скачанного файла (default 1b минимум)
set wGetSiz=1
::  wDir - общее рабочее место
set wDir=\Jobs
::  sDir - общее складское хранилище
set sDir=\Stor
::  jDir - место для оперативных файлов
set jDir=%wDir%\#%nJob%
::  jBad - список проблемных файлов процесса
set jBad=%jDir%\]%nJob%.bad
::  jLst - список итогов процесса (данные для сборки таблиц siTopAuthors)
set jLst=%jDir%\]%nJob%.lst
::  hDir - штатный накопитель оперативных статистик к расчёту
set hDir=%wDir%\#bStat
::  jRpt - общий сводный отчёт с итоговыми сводками процессов
set jRpt=%wDir%\_siTop.#
::  zDir - складское хранилище справочников рассчётов и окрестностей...
set zDir=%sDir%\wOld
::  zStL - общий подкаталог для посуточных подкаталогов с итоговыми списками
set zStL=%zDir%\%jDat%
if NOT exist %jDir%\ mkDir %jDir%
if errorLevel 1 (
  echo fail mkDir %jDir%
  exit
)

:: - 0-й шаг: надо два соседних "v.bStat" за один месяц /"адекватных" - с aHits за тот же день/
set itog=%wDir%\*.v.mig.bStat
call :lookFor %wDir%\*.v.mig.bStat
:: текущего нет? - досвидания
if NOT defined found exit
::  datJ - это дата расчёта /последний полный день в статистиках
set datJ=%found:~0,10%
:: если это ПОСЛЕДНИЙ день месяца, то считать НЕЛЬЗЯ: свежий aHits - граничный! Вот и проверим...
set tmp0=\"%datJ:~0,4% %datJ:~5,2% %datJ:~8,2% 24 59 59\"
set tmp1=%datJ:~5,2%
for /F %%A in (
  'gawk.exe "BEGIN{d=0;if((strftime(\"%%m\",mktime(%tmp0%)+1))==\"%tmp1%\")d++;print(d)}"'
) do set tmp0=%%A
if %tmp0% == 0 (
  echo %datJ% - LAST day of month.%tmp1%
  exit
)
::  sLst - опорный список bStat 
set sLst=%wDir%\%datJ%.v.mig.bStat
echo === cur datJ: %datJ%

:: - 1-й шаг: ищется сортированый по посещаемости список разделов /есть == есть интервал, нет - смотрим расклад/
set /a step+=1
set itog=%jDir%\%datJ%.??.gAuthH
call :lookFor %itog%
if defined found goto gAuthH_done
echo ? check dates and so on...
set modG=1
for /F "tokens=*" %%A in (' gAwk.exe -f %awkH% ') do call :echoH %%A
set itog=%gDif%
set j2do=gAuthH
rem goto mkReq

:mkReq п/п: ЗАПРОС на действие /%j2do%/ + ВЫЗОВ оного + ПРОВЕРКА итога /%itog%/ + ПЕРЕХОД дальше + ОЖИВЛЯЖ
echo. -%step%- HAVE to make: %j2do%
set tmp0=
echo ?ok to goOn [yes] == y
:: !!!
if NOT %step% == 1 if NOT defined errInf goto reqOk
set /p tmp0=?[no]?
:: "y/Y" - "да" - идём делать упомянутое, иначе - досвидания
if /i NOT "%tmp0%" == "y" echo job canceled & exit
:reqOk
call :make_%j2do%
if defined errInf echo %errInf% & goto :eof
if NOT exist %itog% echo ! fail make %itog% & exit
:: !!!
if NOT defined errF if %step% GTR 4 start D:\MEW11\Mew32.exe %itog%
goto %j2do%_done

:echoH разбор выдачи %awkH%
set gapD=%1
set gNum=%2
set nErr=%3
set gDif=%4
set found=%~n4
if NOT "%1" == "]" if NOT "%1" == "gAuthH" goto :eof
echo %*
:: ! проблема - досвидания!
if "%1" == "]" exit
goto :eof

:gAuthH_done  gapD - текущий интервал "намедни", ergo можно указать имена всеъ потребных списков
set /a step+=1
set gapD=%found:~11%
set gDif=%jDir%\%datJ%.%gapD%.gAuthH
set gSta=%jDir%\%datJ%.%gapD%.gAuthS
set gInd=%jDir%\%datJ%.%gapD%.gAuthI
set gTxt=%jDir%\%datJ%.%gapD%.gAuthT
set gTop=%wDir%\%datJ%.%gapD%.gAuth
set gNum=
for /F "tokens=1,*" %%A in (
  ' gawk.exe "END{match($0,/^;5G .+ ([0-9]+):gNum /,a);print(a[1])}" %gDif% '
) do set gNum=%%A
if NOT defined gNum echo ! NO gNum in %gDif% & exit
:: - 2-й шаг: сборка статистик разделов 100+ составление списка текстов 200+
set itog=%gSta%
call :lookFor %itog%
if defined found goto gAuthS_done
set j2do=gAuthS
goto mkReq

:gAuthS_done - 3-й шаг: сборка титулов подходячих разделов составление списка недастающих атрибутов разделов
set /a step+=1
set nTxL=
for /F "tokens=1,*" %%A in (
  ' gawk.exe "END{match($0,/^;5H .+ ([0-9]+):nTxL /,a);print(a[1])}" %gSta% '
) do set nTxL=%%A
if NOT defined nTxL echo ! NO nTxL in %gSta% & exit
set itog=%gInd%
call :lookFor %itog%
if defined found goto gAuthI_done
set j2do=gAuthI
goto mkReq

:gAuthI_done - 4-й шаг: сборка текстов 200+ составление списка раздел-текст
set /a step+=1
set nTit=
for /F "tokens=1,*" %%A in (
  ' gawk.exe "END{match($0,/^;5I .+ ([0-9]+):nTit /,a);print(a[1])}" %gInd% '
) do set nTit=%%A
if NOT defined nTit echo ! NO nTit in %gSta% & exit
set itog=%gTxt%
call :lookFor %gTxt%
if defined found goto gAuthT_done
set j2do=gAuthT
goto mkReq

:gAuthT_done все рабочие списки есть, смотрим/собираем итоговый...
set /a step+=1
set itog=%gTop%
call :lookFor %gTop%
:: ЕСЛИ всё УЖЕ сделано - досвидание
if defined found echo == OK, all is done, ITOG: %gTop% & exit
set j2do=gAuth
goto mkReq

:gAuth_done
goto :eof
exit

:: потребные для работы п/п

:lookFor %1 == %itog% - смотрим - есть ли такой файл \с маской\
set found=
for %%J in ( %itog% ) do if exist %%J (set found=%%~nJ&set tmp0=%%J)
if defined found (
  echo = ok: %tmp0%
) else (
  echo x NO: %itog%
)
goto :eof

:make_gAuthH сборка списка *.gAuthH /самые посещаемые разделы по разнице хитов 100+ в aHits/
echo ? make *gAuthH list...
:: чистим рабочий каталог от уже устаревших файлов
if exist %jDir%\*.* erase /Q/F %jDir%\*.*
if errorLevel 1 echo ! fail erase %jDir%\*.* & goto :eof
set modG=0
gAwk.exe -f %awkH% 
goto :eof

:make_gAuthS сборка списка *.gAuthS /докомплектация и обработка статистик 100-200+/
echo checkGet %gNum% stats
:: взведём таймер со компанией
call :setTimer %gNum%
:: сборка комплекта нужных статистик
for /F %%J in (
  ' gawk.exe "{if(match($0,/^ *([0-9]+):.+:([^ !:]+):/,a)&&(100-a[1]<=0)){print(a[2])}else{exit}}" %gDif% '
) do (
  call :chkGet %%J /stat.shtml stat %jDir%
  if defined errInf goto :eof
)
rem остановим "таймер" качалки время работы в секундах
for /F %%J in ('gawk.exe "BEGIN{print(strftime(\"%%M:%%S\",systime()-%ratW%,-1));exit}"') do set ratW=%%J
echo.
echo. == %gNum% %ratW% ? make *gAuthS list...
:: построение списка *gAuthS
gAwk.exe -f %awkS% %jDir%\*.stat.htm
goto :eof

:make_gAuthI сборка списка *.gAuthI /комплектация и обработка Титулов разделов 200+/
echo checkGet %nTxL% Titles
:: взведём таймер со компанией
call :setTimer %nTxL%
:: сборка комплекта нужных титулов
for /F "tokens=*" %%J in (
  ' gawk.exe "{if(match($0,/:!:([^ !:]+):!:./,a))print(a[1])}" %gSta% '
) do (
  call :chkGet %%J / titl %jDir%
  if defined errInf goto :eof
)
rem остановим "таймер" качалки время работы в секундах
for /F %%J in ('gawk.exe "BEGIN{print(strftime(\"%%M:%%S\",systime()-%ratW%,-1));exit}"') do set ratW=%%J
echo.
echo. == %nTxL% %ratW% ? make *gAuthI list...
:: построение списка *gAuthI
gAwk.exe -f %awkI% %jDir%\*.titl.htm
goto :eof

:make_gAuthT сборка списка *.gAuthT //докомплектация текстов 200+/
echo checkGet %nTit% Texts
:: вычленим актуальный путь в складу текстов "к расчёту" (like: U:\Stor\Text\2020.11.18.text\)
rem ;2D =Ср=  998:tNum OkV:99.8% mod:v 2022.04.09:dEnd 0:sNul 2022.04.13:done 11:14 00:16:09~wrks 0~ratW 0:dnLd 
set tDir=
for /F %%J in (
  ' gawk.exe  "END{match($0,/^;2D =.+ (20[\.0-9]{8}):done /,a);print(a[1])}" %sLst% '
) do set tDir=%sDir%\Text\%%J.text
if NOT defined tDir set errInf=! NO ";2D ... :done" in %sLst% & goto gAuth_err
if NOT exist %tDir% mkDir %tDir%
if errorLevel 1 (
  echo fail mkDir %tDir%
  exit
)
::  tmpF времянка для текстов
set tmpF=%jDir%\tmp.text.html
::  tmpL пустая времянка для списка
set tmpL=%jDir%\tmp.text.lst
echo. >nul 2> %tmpL%
:: взведём таймер со компанией
call :setTimer %nTit%
:: сборка комплекта нужных титулов
for /F "tokens=1,2*" %%J in (
  ' gawk.exe "{if(match($0,/:!:([^ !:]+):!:([^ !:]+) ./,a))print(a[1],a[2])}" %gInd% '
) do (
  call :chkGet %%J /%%K.shtml text %tDir% %%K
  if defined errInf goto :eof
)
rem остановим "таймер" качалки время работы в секундах
for /F %%J in ('gawk.exe "BEGIN{print(strftime(\"%%M:%%S\",systime()-%ratW%,-1));exit}"') do set ratW=%%J
echo.
echo. == %nTit% %ratW% ? make *gAuthT list...
for /F "tokens=1,2*" %%J in (
  ' gawk.exe "{if(match($1,/^20[\.0-9]{8}/)){n++}else{if($1==\"]\")e++}}END{print(n,0+e)}" %tmpL% '
) do (
  set nTxt=%%J
  set nErr=%%K
)
echo ;   begD       endD     sizT.Ok "namA" "namT" "votT" "jnrT" fNam fTmp >>               %tmpL%
gawk.exe "{if(match($1,/^;[0-9][^ ]/,a))print($0)}" %gInd% >>                               %tmpL%
echo ;5T txtL %datJ%:datJ %gapD%:gapD %nTxt%:nTxt %nErr%:nErr %jDat%:done at~%time:~0,5% >> %tmpL%
move /y %tmpL% %gTxt%
if errorLevel 1 set errInf=! fail move %tmpL% %gTxt% & goto gAuth_err
echo. == %nTxt%:nTxt %nErr%:nTxt
goto :eof

:make_gAuth  сборка списка *.gAuth /итоговый: "всё в одном"/
for /F "tokens=*" %%A in ( ' gawk.exe -f %awk_% ' ) do call :mkRpt %%A
if NOT defined errInf echo == OK all is done, itog: %gTop%
goto :eof

:mkRpt
if %1 == ] set errInf=fail %awk_% & goto :eof
echo ;5Q gapQ %* %jDat%:done
echo ;5Q gapQ %* %jDat%:done >> %gTop%
echo ;5Q gapQ %* %jDat%:done >> %jRpt%
copy /b /y %gTop% %zStL% >nul
if errorLevel 1 set errInf=fail copy %gTop% 2 %zDir%
goto :eof

:setTimer cNum
set cNum=%1
set /a jMax=%cNum%/100
if NOT %cNum:~-2% == 00 set jMax=%jMax%+
set jMax=%jMax%      .
set jMax=%jMax:~0,4%
set cNum=0
for /F %%J in ('gawk.exe "BEGIN{print(systime());exit}"') do set ratW=%%J
::  dnLd - счётчик /формально/ успешно скачанного (НЕ 0b)
set dnLd=0
goto :eof

rem           id_P.1 2          3     4    
rem call :chkGet %%A /stat.shtml stat %jDir%
rem call :chkGet %%A /%%B.shtml text %tDir% %%B
:chkGet id_W wSuf
set /a cNum+=1
set id_P=%1
:: если ненужен - просто "/" без кавычек /скачивание титулов разделов/
set wSuf=%2
set fSuf=%3
set outD=%4
set outT=%5
set id_F=%id_P:~0,1%~%id_P:~2%
if defined outT set id_F=%id_F%.%outT%
:: имя локального файла из authID (d/dront -> d~dront)
set fSuf=%id_F%.%fSuf%.htm
if defined outT set fSuf=%fSuf%l
set wFil=%outD%\%fSuf%
set /a count+=1
:: возможно уже есть?
set mark=.
set mrk1=:
if exist %outD%\%fSuf% goto show
:: нет, а в статистиках?
set mark=*
set mrk1=0
if exist %hDir%\%fSuf% (
  copy /b /y %hDir%\%fSuf% %jDir% >nul
  if errorLevel 1 set errInf=fail copy %id_F%.stat.htm 2 %jDir% & goto :eof
  goto show
)
:: и там нет, нужно скачивать...
set mark=~
set mrk1=!
set wUrl=%siUrl%/%id_P%%wSuf%
set gFil=%wFil%
if NOT defined outT goto chk_Get
set gFil=%tmpF%
if exist %tmpF% erase %tmpF%
if errorLevel 1 set errInf=fail erase %tmpF% & goto :eof
:chk_Get
call wGetUrl2fil.cmd %wUrl% %gFil%
if defined errInf goto :eof
if NOT %wGetSiz% == 0 goto show
:: если титул раздела недоступен...
echo ] nulFil %wUrl% >> %jBad%
set /a nulP+=1

:show оживляж + конкретная доп обработка текстов /чистка+дата_изменения/
if %cNum:~-1% == 0 set mark=%mrk1%
gawk.exe "BEGIN{printf(\"%mark%\");exit}"
set tmp0=    %cNum%
set tmp0=%tmp0:~-5,3%.%jMax%
if "%cNum:~-2%" == "00" (
  gawk.exe "BEGIN{printf(\" %%s\",\"%tmp0%\"strftime(\"%%M:%%S\",systime()-%ratW%,-1));exit}"
  echo.
)
:: если это НЕ текст - всё
if NOT defined outT goto :eof

:: если текст у нас /и должного вида/ нужно лишь узнать его атрибуты
if exist %outD%\%fSuf% (
  set fOut=
  set tmp0=gAwk.exe -f %awkT% %outD%\%fSuf%
  goto readTxt
)
:: если в СИ нет - всё
if  %wGetSiz% == 0  goto :eof
:: иначе +преобразование если что-то скачалось
set fOut=%outD%\%fSuf%
set tmp0=gAwk.exe -f %awkT% %tmpF%
:readTxt
%tmp0% >> %tmpL%
:: уже был раньше?
if NOT defined fOut goto :eof
:: или преобразование провалилось?
if NOT exist %outD%\%fSuf% set errInf=fail %awkT% %tmpF% to %fOut% & goto :eof
:: нет+стало == для свежескачанного надоть дату конечного результата вструмить...
set fOut=
:: оную выяснив сперва
for /F "tokens=1,2,*" %%J in (' gawk.exe -f %awkT% %outD%\%fSuf% ') do set fOut=%%K
:: выяснили?
if NOT defined fOut set errInf=fail gawk with %outD%\%fSuf% & goto :eof
:: не ошибка?
if %fOut:~0,3% == err set errInf=fail gawk with %outD%\%fSuf% & goto :eof
:: порправляем дату на нужную
touch.exe -t %fOut:~0,4%%fOut:~5,2%%fOut:~8,2%0100 %outD%\%fSuf%
goto :eof

:: 2020.11.04, nvv, creation
:: 2020.11.13, nvv, будКлуб - глупость
:: 2020.11.28, nvv, процесс делает вроде-бы всё нужное, хить и с заглушками ;(!!! без сети)...
:: 2021.01.15, nvv, всё работает +несколько лидеров в разделе учитываются без фиксации
::     +текст с "ограниченным доступом" в разделе => раздел исключён из списка
:: 2021.02.02, nvv, minor turning
:: 2021.02.12, nvv, minor turning (gNum)
:: 2021.03.15, nvv, minor turning (gNum)
:: 2021.03.26, nvv, minor turning (tmpF)
:: 2021.04.09, nvv, *.bStat turned to *.lst.bStat
:: 2021.05.11, nvv, очередная оптимизация с унификацией...
:: 2021.06.02, nvv, minor turnings {job's timing added - ratW & wrks)
:: 2021.10.11, nvv, minor turnings {в пакетном режкме на экране выбранные aHits}
:: 2021.11.20, nvv, ещё одно улучшение оживляжа-с ;)
:: 2021.12.10, nvv, ещё одно улучшение оживляжа-с ;)
:: 2022.01.01, nvv, ещё одно улучшение оживляжа-с ;)
:: 2022.01.19, nvv, исправлена неточность в выдачи ошибки в работе gAuthH.awk
:: 2022.02.26, nvv, исправлена неточность в выдаче объёмов раздела/произведения (1.1Gb и 17 Mb однако...)
:: 2022.03.04, nvv, изменена реакция на проблемы с aHits - теперь просто выход с уведомлением
:: 2022.03.31, nvv, времянка minL из-за силных пертурбаций учёта в марте
:: 2022.04.17, nvv, радикально переделано: унифицировано и оптимизировано
:: 2022.06.24, nvv, исправлена неточность контроля успеха чистки скачанного текста
:: 2022.08.03, nvv, if datJ == ПОСЛЕДНИЙ день месяца, then свежий aHits - со СКАЧКОМ /непригоден для/
:: 2022.08.19, nvv, отключены промежуточные: запросы на продолжение работы и выдача результатов в MEW

    Имя файла скрипта: "gAuth_.awk" V-текст скрипта-V
- - - - - - - - -
BEGIN{                                      #  gAuth.awk сборка итогового списка для siTopAuthors
#  2022.04.13, nvv, creation
#  2023.07.28, nvv, rearrange inpChk & outPut
# test=1
  bRd=":!:"                                       #  разделитель блоков в строке
  tIni=systime()
  for(str2 in ENVIRON){                       # ! регистр букв в имени переменной может быть любой
    str1=tolower(str2)                            # Temp и temp - РАЗНЫЕ имена для gAwk...
    if(str1=="gsta") gSta=ENVIRON[str2]           #  исходный список
    if(str1=="gind") gInd=ENVIRON[str2]           #  исходный список
    if(str1=="gtxt") gTxt=ENVIRON[str2]           #  исходный список
    if(str1=="gtop") fOut=ENVIRON[str2]           #  исходный список
  }
  if(gSta==""){
    if(test){
      jj="2023.07.25.3"
      gSta="\\Jobs\\#gAuth\\"jj".gAuthS"
      gInd="\\Jobs\\#gAuth\\"jj".gAuthI"
      gTxt="\\Jobs\\#gAuth\\"jj".gAuthT"
      fOut=jj".gAuth"
    }else{
      errS="] no envVar 'gSta'"
      exit
    }
  }

#  формат списка gSta, нужное: id_P txtL txtG id_T hitP numP new
#    277  0   277 0  58367     29    1159     3 :!:k/kadawr:!:n "Псион":!:"Кадавр" "no•comments"
# ;txtL txtG.2 Luck new.4 hitP numP.6 sizK sizN.8 ! id_P ! id_T.10 "namT" ! "namP".12 "moto"
#  <- - - - - - - block.1 - - - - - - - - - - ->   block.2 <- block.3 ->    <- block.4 ->
  if(test)printf("load gAuthS ... ")
  while((getline line<gSta)>0){
    jj=split(line,aj,bRd)
    if(jj==4){                                    #  строка раздела
      id_P=aj[2]                                  #  его ID
      aStH[id_P]=aj[1]                            #  txtL txtG Luck new hitP numP sizK sizN
      aStT[id_P]=aj[3]                            #  id_T "namT"
      aStP[id_P]=aj[4]                            #  "namP" "moto"
      nSta++                                      #  счётчик разделов-кандидатов в рейтинг
    }
  }
  close(gSta)
  if(test)print("nSta:"nSta)

#  формат списка gInd, нужное: id_P datP sizK sizN "namP" "moto"
# 2021.09.22   1159     3:!:k/kadawr:!:n "Псион":!:"Кадавр" "no•comments"
# ; datP       sizK  sizN ! id_P ! id_T "namT" ! "namP" "moto"
  if(test)printf("load gAuthI ... ")
  while((getline line<gInd)>0){
    jj=split(line,aj,bRd)
    if(jj==4){                                    #  строка раздела
      id_P=aj[2]                                  #  его ID
      aInH[id_P]=aj[1]                            #  datP sizK sizN
      aInT[id_P]=aj[3]                            #  id_T "namT"
      aInP[id_P]=aj[4]                            #  "namP" "moto"
      nInd++                                      #  счётчик разделов-кандидатов в рейтинг
#     }else{                                        #  строка подвала
#       if(match(aj[1],/^;[0-9]/))aEnd[++nEnd]=line                           #  прикопаем...
    }
  }
  close(gInd)
  if(test)print("nInd:"nInd)

#  формат списка gTxt, нужное: id_P id_T sizT endD "namT" "jnrT"
#  2014.07.10 2017.11.30  1821.1 "Скс." "Режим•бога" 0 "<a•href=/type/index_type_1-1.shtml>Роман</a>:"  s~sks.111.text
# ;   begD.1     endD.2   sizT.Ok "namA".4 "namT" "votT".6 "jnrT" fNam.8 fTmp
  if(test)printf("load gAuthT ... ")
  while((getline line<gTxt)>0){
    split(line,aj)
    if(match(aj[1],/^[0-9\.]{10}$/)){             #  строка текста раздела
      split(aj[8],ak,".")
      id_P=substr(ak[1],1,1)"/"substr(ak[1],3)    #  его ID
      if(match(aj[3],/^[0-9]+.1$/)){              #  нам недоступные тексты неинтересны
        aTxt[id_P]=line                   #  begD endD sizT.Ok "namA" "namT" "votT" "jnrT" fNam fTmp
        aTxI[id_P]=ak[2]                            #  id_T
        aTxF[id_P]=aj[8]                            #  id_F
        nTxt++                                      #  счётчик разделов-кандидатов в рейтинг
      }
    }else{                                        #  блок в подвале
# ;1aPre hits 5:xNam 451:xNum 104684:xPas 2023.07.20:hDat 522207:hMax 1052:hPag 12:add2 388:h30k 1058:dnLd 0.08~ratW 2023.07.22:done 01:19:32~wrks 15:03 
# ;1aCur hits 5:xNam 451:xNum 104693:xPas 2023.07.23:hDat 525898:hMax 1052:hPag 1250:add2 390:h30k 1076:dnLd 0.18~ratW 2023.07.25:done 04:22:17~wrks 15:03 
# ;5G gapP 2023.07.22:datJ 3:gapD 1646:gNum 0:nErr 100:minL 1248:newH 2023.07.25:done at~15:43
# ;5H txL1 2023.07.22:datJ 3:gapD 122:nTxL 0:nErr 200:minT 0:txtD 2023.07.25:done at~15:55
# ;5I titl 2023.07.22:datJ 3:gapD 122:nTit 0:nErr 2023.07.25:done at~15:57
# ;5T txtL 2023.07.22:datJ 3:gapD 122:nTxt 0:nErr 2023.07.25:done at~15:57 
      if(match(aj[1],/^;[0-9][^ ]/))aEnd[++nEnd]=line   #  прикопаем...
#  соберём данные для отчёта
      if      (match(line,/^;1aPre .+ ([0-9\.]+):hDat /,aj)){
        dat1=aj[1]
      }else if(match(line,/^;1aCur .+ ([0-9\.]+):hDat /,aj)){
        dat2=aj[1]
      }else if(match(line,/^;5G .+ ([0-9]+):gNum ([0-9]+):nErr .+ ([0-9]+):newH /,aj)){
        gNum=aj[1]
        nErr=aj[2]
        newH=aj[3]
      }else if(match(line,/^;5H .+ ([0-9]+):nTxL ([0-9]+):nErr /,aj)){
        nTxL=aj[1]
        nErr=nErr+aj[2]
      }else if(match(line,/^;5I .+ ([0-9]+):nTit ([0-9]+):nErr /,aj)){
        nTit=aj[1]
        nErr=nErr+aj[2]
      }else if(match(line,/^;5T .+ ([0-9]+):nTxt ([0-9]+):nErr /,aj)){
        nTxt=aj[1]
        nErr=nErr+aj[2]
      }
    }
  }
  close(gTxt)
  if(test)print("nTxt:"nTxt)
  if(nSta*nInd*nTxt==0){
    print("] empty or bad any of above!")
    exit
  }

# gHit: id_P hitP numP new
# gSta: id_P txtL txtG id_T hitP numP new
# gInd: id_P datP sizK sizN "namP" "moto"
# gTxt: id_P id_T sizT endD "namT" "jnrT"

# gSta: aStH[id_P] # txtL txtG Luck new hitP numP sizK sizN
# gInd: aInH[id_P] # datP sizK sizN
# gInd: aInP[id_P] # "namP" "moto"
# gTxt: aTxt[id_P] # begD endD sizT.Ok "namA" "namT" "votT" "jnrT" fNam fTmp

  for(id_P in aTxt){
    split(aStH[id_P],aS1)                         #  txtL txtG.2 Luck new.4 hitP numP.6 sizK sizN
    split(aInH[id_P],aI1)                         #  datP sizK.2 sizN
    split(aInP[id_P],aI2)                         #  "namP" "moto".2
    split(aTxt[id_P],aT1)         #  begD endD.2 sizT.Ok "namA".4 "namT" "votT".6 "jnrT" fNam fTmp
    aOut[++nOut]=sprintf("%5s %2s",aS1[1],aS1[2]) bRd    #  sort key
    aOut[nOut]=aOut[nOut] sprintf("%6s ",aS1[3])      # Luck
    aOut[nOut]=aOut[nOut] sprintf("%1s ",aS1[4])      # new
    aOut[nOut]=aOut[nOut] sprintf("%7s ",aS1[5])      # hitP
    aOut[nOut]=aOut[nOut] sprintf("%6s ",aS1[6])      # numP
    aOut[nOut]=aOut[nOut] sprintf("%7s ",aI1[2])      # sizK
    aOut[nOut]=aOut[nOut] sprintf("%5s ",aI1[3])      # sizN
    aOut[nOut]=aOut[nOut] sprintf("%5s ",aS1[1])      # txtL
    aOut[nOut]=aOut[nOut] sprintf("%2s ",aS1[2])      # txtG
    aOut[nOut]=aOut[nOut] sprintf("%7s ",aT1[3])      # sizT
    aOut[nOut]=aOut[nOut] sprintf( "%s ",aI1[1])      # datP
    aOut[nOut]=aOut[nOut] sprintf( "%s ",aT1[2])      # endD
    aOut[nOut]=aOut[nOut] sprintf( "%s ",id_P  )      # id_P
    aOut[nOut]=aOut[nOut] sprintf( "%s ",aI2[1])      # "namP"
    aOut[nOut]=aOut[nOut] sprintf( "%s ",aI2[2])      # "moto"
    aOut[nOut]=aOut[nOut] sprintf( "%s ",aTxI[id_P])  #  id_T
    aOut[nOut]=aOut[nOut] sprintf( "%s ",aT1[5])      # "namT"
    aOut[nOut]=aOut[nOut] sprintf( "%s ",aT1[7])      # "jnrT"
    aOut[nOut]=aOut[nOut] sprintf( "%s ",aTxF[id_P])  #  id_F
  }
#  старый фомат конечного списка gAuth
#   470  0 13756  139 3848    9  475  1 1821.1 2019.06.23 2017.11.30 s/sks "Скс" "Режим•бога" 111 "Режим•бога" "<a•href=/type/index_type_1-1.shtml>Роман</a>:" s~sks
# :Luck new hitP numP sizK sizN hitT txtN sizT   datP       endD id_P "namP" "moto" id_T "namT" "jnrT" fNam
#  %%7s %%2s %%8s %%7s %%8s %%6s %%6s %%3s %%8s
# :formItA %luck% %typH% %hitP% %numP% %sizK% %sizN% %hitT% %txtN% %sizT%
# gawk.exe "BEGIN{printf(\"%%7s%%2s%%8s%%7s%%8s%%6s%%6s%%3s%%8s\",%1,%2,%3,%4,%5,%6,%7,%8,%9);exit}" >> %jLst%
# echo. %datP% %endD% %id_P% %namP% %moto% %id_T% %namT% %jnrT% %fNam% >> %jLst%
  asort(aOut)
  for(jj=nOut;jj>0;jj--){
    split(aOut[jj],aj,bRd)
    print(aj[2]) >fOut
  }
  print(\
";Luck.1 new hitP.3 numP sizK.5 sizN txtL.7 txtG sizT.Ok.9  datP    endD.11   id_P \"namP\".13 \"moto\" id_T.15 \"namT\" \"jnrT\".17 fNam"\
)                >fOut
  for(jj=1;jj<=nEnd;jj++)print(aEnd[jj]) >fOut    #  <- старый подвал + свежак -v
  exit
}
# ;Luck new.2  hitP numP.4   sizK sizN.6 hitT txtN.8 sizT  datP.10   endD id_P.12 "namP" "moto".14 id_T "namT".16 "jnrT" fNam.18
#     290 0   12266    141    3848     9   293  1  1821.1 2019.06.23 2017.11.30 s/sks "Скс" "Режим•бога" 111 "Режим•бога" "<a•href=/type/index_type_1-1.shtml>Роман</a>:" s~sks
END{
  if(errS!=""){
    print(errS)
    exit -1
  }
#  выжимка для отчёта
# 5A 2023.07.23:dat1 2023.07.27:dat2 600:gNum 229:newH 118:nTxL 117:nTit 117:nTxt 1:nErr
  print(dat1":dat1",dat2":dat2",gNum":gNum",newH":newH",nTxL":nTxL",nTit":nTit",nTxt":nTxt",nErr":nErr")
}

    Имя файла скрипта: "gAuthH.awk" V-текст скрипта-V
- - - - - - - - -
BEGIN{ # gAuthH.awk - прирост хитов двух последних списков aHits, Out - сортированный список дифов
# если нужные переменные среды не установлены, работает автономно, результат - в текущем каталоге
#  интервал дат (т.е. число дней в нём) вычисляется по успешным полным сборкаь bStat ("mod:v")

# 2022.08.29, nvv, для сравнения нужны ВСЕ разделы из СТАРОГО списка, в т.ч. без текстов и ника
# ! прочая история в подвале
  extH="gAuthH"                                   #  тип работы (расширение полного списка дифов)
  bRd=":!:"                                       #  разделитель блоков в строке
  newH=0                                          #  раздела нет в предыдущем aHits
  nErr=0

  for ( j2 in ENVIRON ) {                         #  ! регистр букв в имени переменной
    j1 = tolower( j2 )                            #  Temp и temp - РАЗНЫЕ имена для gAwk...
    if (j1=="jrpt") jRpt=ENVIRON[j2]              #  сводный отчёт по рассчётам siTopTexts
    if (j1=="zdir") zDir=ENVIRON[j2]"\\"          #  склад сводок и выборок к рассчётам
    if (j1=="jdir") jDir=ENVIRON[j2]"\\"          #  рабочая папка
    if (j1=="modg") modG=ENVIRON[j2]              #  0-список, 1-оценка
    if (j1=="minl") minL=ENVIRON[j2]      #  минимальные хиты в сутки для проверки текстов раздела
  }
  if(jRpt==""){
    jRpt="..\\Jobs\\_siTop.#"                     #  общий лог расчётов
    zDir="..\\Stor\\wOld\\"                       #  общий склад списков
    modG=2                                        #  0-список, 1-оценка, 2-локальное_тестирование
    minL=100                                      #  граница интереса (10 хитов в сутки)
  }
# print "### "modG
  while((getline line<jRpt)>0){                   #  ищем нужные нам даты в _siTop.#
# ;1A hits 2:xNam 445:xNum 101285:xPas 2021.04.22:hDat 1018:hPag 806772:hMax 1018:DnLd 2021.04.27:done 25:mins gapT~14:53-15:18~
    if(match(line,/^;1A .+ (20[\.0-9]{8}):hDat .+ (20[\.0-9]{8}):done /,arr0)){
      preF=curF
      preD=curD
      curF=arr0[1]                                #  datF - дата в имени нужного aHits
      curD=arr0[2]                                #  datD - дата в имени каталога с нужным aHits
    }
# ;2D =Пт= 1525:tNum OkV:99.9% mod:v 2021.06.01:dEnd 0:sNul 350:dnLd 1.11~ratW 2021.06.04:done 00:25:35~wrks 9:56
    if(match(line,/ mod:v (20[\.0-9]{8}):dEnd .*(20[\.0-9]{8}):done/,arr0)){
      if(datJ!=arr0[1]){
        preS=datJ
        preJ=curJ
        datJ=arr0[1]
        curJ=arr0[2]
      }
    }
  }
  close(jRpt)

if(strftime("%Y.%m.%d")=="2021.09.29"){           #  после паузы в обновлениях
  preF="2021.09.16"
  preD="2021.09.17"
}
if(strftime("%Y.%m.%d")=="2021.07.21"){           #  после паузы в обновлениях
  preF="2021.07.08"
  preD="2021.07.09"
  preS="2021.07.06"
}
# curF=arr0[1]                                #  datF - дата в имени нужного aHits
# curD=arr0[2]                                #  datD - дата в имени каталога с нужным aHits
# datJ=arr0[1]

  if(modG)print(extH" 1aPre: "preF" aHits at: "preD)
  if(modG)print(extH" 1aCur: "curF" - ' - at: "curD)   #  строим полное имя нужного списка
  if(modG)print(extH" 2bPre: "preS" bStat")
  if(modG)print(extH" 2bCur: "datJ" - ' -")

  if(preD!=preJ){                                 #  предыдущие НЕ-парные
    errS="] DIF preDats: aHits:"preD"!="preJ":preJ (jRpt:"jRpt")"
    exit 1
  }
  if(curD!=curJ){                                 #  текущие НЕ-парные
    errS="] DIF curDats: aHits:"curD"!="curJ":curJ (jRpt:"jRpt")"
    exit 1
  }
  if(substr(preS,1,7)!=substr(datJ,1,7)){     #  на стыке месяцев нельзя (скачок V хитов "12мес.")
    errS="] DIF months: "substr(preS,1,7)":preS != "substr(datJ,1,7)":datJ (jRpt:"jRpt")"
    exit 1
  }

  gapD=(mkSecs(datJ)-mkSecs(preS))/86400          #  интервал (в днях) цикла обновления статистик
  preF=zDir preD"\\"preF".aHits"                  #  полное имя предыдущего aHits
  curF=zDir curD"\\"curF".aHits"                  #  полное имя текущего aHits

  fOut=jDir datJ"."gapD"."extH                    #  итоговый список "2020.11.14.3.gAuthH"
#   if(modG)print("fOut:  "fOut)
  if(modG)print(extH" loading preHits "preF" ... ")
#      1     2       3      4  5        6        7
# 441971     6    1077      3 k/kadawr "Кадавр" "no•comments"
# hitP.1 numP.2 sizK.3 sizN.4 id_P.5 "namP".6 "moto".7
# загрузим опорный (aka предыдущий) список "разделы-хиты"
  preN=0
  while ((getline line < preF) > 0) {
    split(line,aj)                        #  v- для сравнения нужны ВСЕ разделы из старого списка
#     if(match(aj[1],/^[0-9]+$/)&&aj[4]&&(aj[6]!="\">?<\"")){ #  нужна запись НЕ пустого раздела
    if(match(aj[1],/^[0-9]+$/)){ #  нужна запись НЕ пустого раздела
      preN++
      a2h[aj[5]]=aj[1]                            #  массив hitP[id_P]
    }else if(aj[1]==";1A")iPre=";1aPre "substr(line,5)
  }
  close(preF)                                     #  всё/не_всё, - старый список тогось
  if(!preN){                                      #  старый список пуст
#     if(modG)print("")
    errS="] NO_or_BAD_preHits:" preF ", (jRpt:"jRpt")"
    exit
  }
  if(modG)print(extH" found preN:"preN)
  if(modG)print(extH" loading curHits "curF" ... ")
# сравним оперативный (aka текущий) список с предыдущим (опорным - выцепим изменения в хитах)
  curN=0
  while ((getline line < curF) > 0) {
    split(line,aj)                                #  разделяем строку на элементы
    if(match(aj[1],/^[0-9]+$/)&&aj[4]&&(aj[6]!="\">?<\"")){ # это строка НЕ пустого раздела
      id_P=aj[5]
      if(id_P in a2h){                            #  был в предыдущем, считаем
        luck=aj[1]-a2h[id_P]                      #  прирост хитов за gapD дней
        typH="0"                                  #  флаг "НЕ новый" раздел в aHits
      }else{
        luck=aj[1]                                #  все накопленные хиты
        typH="1"                                  #  флаг "НОВЫЙ" раздел
        newH++
      }
      luck=sprintf("%7.1f",10*luck/gapD)          #  удача*10 раздела "начерно"
      split(luck,ak,".")
      luck=ak[1]                              #  нам нужна целая часть (без дробной после запятой)
      if(luck-30000>=0){                          #  3000+ посетителей в день - криминал!
        nErr++
        if(modG){
          print(extH" ] TOO high Luck*10: "luck" for numP:"aj[2]", id_P:"id_P", skipped... ")
        }else{
          aErr[nErr]="] TOO high Luck*10: "luck" for numP:"aj[2]", id_P:"id_P
        }
# !!! возможно придётся переобустраивать этот момент...       exit
      }else if(luck-minL>=0){       #  удачи "начерно" достаточно для более внимательной проверки
        hitP=aj[1]
        numP=aj[2]
        sizK=aj[3]
        sizN=aj[4]
        gNum++
        aDif[id_P]=sprintf("%5s"bRd"%s %6s %6s %7s %5s"bRd"%s"\
,luck,typH,hitP,numP,sizK,sizN,aj[6]" "aj[7])
      }
    }else if(aj[1]==";1A")iCur=";1aCur "substr(line,5)
  }
  close(curF)                                     #  всё/не_всё, - новый список обработан
  if(!gNum){                                      #  старый список пуст
#     if(modG)print("")
    errS="] NO_or_BAD_curHits:" curF ", (jRpt:"jRpt")"
    exit
  }
  if(modG)print(extH" == gNum:"gNum", gapD:"gapD", nErr:"nErr)
#  формат конечного списка
#    2456:!:0     53231  38     312         8:!:f/fox:!:"Fox" "Раздел..."
# ;Luck.1 ! new hitP.3 numP sizK.5 sizN ! id_P.7 ! "namP" "moto".9
#  block.1  <- - - - block.2 - - - - ->   block.3  <- block.4 - ->
  for(j1 in aDif){
    j2=split(aDif[j1],aj,bRd)                        #  разделим строку на блоки
# if(j2-3>0)print("] ado in namP+moto for id_P:"j1) #  в строке больше трёх блоков !!!
    aOut[++nOut]=aj[1] bRd aj[2] bRd j1 bRd aj[3] #  и соберём для сортировки и выдачи в файл
  }
  if(modG)print(gapD,gNum,nErr,fOut)
  if(modG==1)exit
  info=";5G gapP "datJ":datJ "gapD":gapD "gNum":gNum "nErr":nErr "minL":minL "newH":newH "strftime("%Y.%m.%d:done at~%H:%M")
  asort(aOut)                                     #  сотртировка по возрастанию(!) "удачи"
  # nnn 15572 #  423515       6   1094     3 k/kadawr "Кадавр" "no•comments"
  for(j1=nOut;j1>0;j1--)print(aOut[j1])                                         >fOut
  print(";Luck.1 ! new hitP.3 numP sizK.5 sizN ! id_P.7 ! \"namP\" \"moto\".9") >fOut
  print(iPre)                                                                   >fOut
  print(iCur)                                                                   >fOut
  print(info)                                                                   >fOut
  if(nErr){
    for(j1=nErr;j1>0;j1--)print(aErr[j1])                                       >fOut".err"
    print(info)                                                                 >fOut".err"
  }
  print(" == gNum:"gNum", gapD:"gapD", nErr:"nErr)
  exit
}

function mkSecs(fNam){
  n1=split(fNam,aj,":")
  n1=split(aj[n1],aj,"\\")
  n1=split(aj[n1],aj,".")
  return(mktime(aj[1]" "aj[2]" "aj[3]" 12 00 00"))
}

END{
  if(errS!=""){
    print("")
    print(errS)
    exit -1
  }
  exit
# 2020.11.01, nvv, creation
# 2020.11.27, nvv, minor improvements +comments adds +;1A-info -> ;1aPre/;1aCur
# 2020.11.29, nvv, изменен формат имены вых.файла fOut curD.datG.
# 2021.02.07, nvv, исправлена неточность в выявлении координат и имён последних *.aHits
# 2021.02.12, nvv, добавлено извещение о числе разделов с "удачей" >=100 (gNum)
# 2021.03.15, nvv, переписаны стартовые операции для корректного вычленения нужных *.aHIts
# 2021.04.27, nvv, дифер занимается именно разницей aHits, прочее - в CMD
# 2021.05.02, nvv, мелкая косметика
# 2021.05.26, nvv, очередное вструмление-с
# 2021.06.09, nvv, мелкое уточнение выявление preS и datJ
# 2021.07.21, nvv, ручное задание дат нижней границы: pre?
# 2021.09.29, nvv, ручное задание дат нижней границы: pre?
# 2021.10.11, nvv, улучшена выдача на экран в пакетном режиме (оживляж-с;)
# 2021.11.20, nvv, ещё одно улучшение оживляжа-с ;)
# 2021.12.07, nvv, ещё одно улучшение оживляжа-с ;)
# 2022.04.18, nvv, 3000+ посетителей в день - криминал! + серьёзно переделано
#  в том числе формат вых.файла (см. переменную bRd)
}

    Имя файла скрипта: "gAuthI.awk" V-текст скрипта-V
- - - - - - - - -
BEGIN{                                            #  gAuthI.awk разбор шапкок разделов СИ
# 2020.11.28, nvv, объём (например "1899k/3") надлежит разделять (например: "1899" + "3")
# 2021.05.02, nvv, мелкая косметика
# 2022.04.17, nvv, радикально переделано 
# 2023.01.25, nvv, сменилось разрешение экрана, слегка меняем оживляж
  if(ARGV[1]=="")exit                             # защита от случайного пуска
  bRd=":!:"                                       #  разделитель блоков в строке
  for(str2 in ENVIRON){                       # ! регистр букв в имени переменной может быть любой
    str1=tolower(str2)                            # Temp и temp - РАЗНЫЕ имена для gAwk...
    if(str1=="gsta") gSta=ENVIRON[str2]           #  исходный список
  }
  if(gSta=="")gSta="\\Jobs\\#gAuth\\2022.04.05.3.gAuthS"
  j0=split(gSta,aj,"\\")
  fOut=aj[j0]
  if(j0==1)fPth=""
  else{
    fPth=aj[1]"\\"
    for(j1=2;j1<j0;j1++)fPth=fPth aj[j1]"\\"
  }
  split(fOut,aj,".")
  datJ=aj[1]"."aj[2]"."aj[3]
  gapD=aj[4]
  fOut=fPth datJ"."gapD".gAuthI"
#  формат входного списка
#    277  0   277 0  58367     29    1159     3 :!:k/kadawr:!:n "Псион":!:"Кадавр" "no•comments"
# ;txtL txtG.2 Luck new.4 hitP numP.6 sizK sizN.8 ! id_P ! id_T.10 "namT" ! "namP".12 "moto"
#  <- - - - - - - block.1 - - - - - - - - - - ->   block.2 <- block.3 ->    <- block.4 ->
  while((getline line<gSta)>0){       #  ищем последнюю запись успешного скачивания этого списка
    jj=split(line,aj,bRd)                            #  !!! причесать по новому!
    if(jj==4){                                    #  строка раздела
      id_P=aj[2]                                  #  его ID
      aIdT[id_P]=aj[3]                            #  его самый посещаемый текст
      aIdP[id_P]=aj[4]                            #  его описание /из aHits/
      nIdP++                                      #  счётчик разделов-кандидатов в рейтинг
    }else{                                        #  строка подвала
      if(match(aj[1],/^;[0-9][^ ]/))aEnd[++nEnd]=line                           #  прикопаем...
    }
  }
  close(gSta)
  int7=(nIdP-(nIdP%100))/100
  if(nIdP%100)int7=int7"+"
  buf=""
  errS=""
  nErr=0
  tIni=systime()
  print("scan "nIdP" titles...")
# ;Luck new.2  hitP numP.4   sizK sizN.6 hitT txtN.8 sizT  datP.10   endD id_P.12 "namP" "moto".14 id_T "namT".16 "jnrT" fNam.18 
#     290 0   12266    141    3848     9   293  1  1821.1 2019.06.23 2017.11.30 s/sks "Скс" "Режим•бога" 111 "Режим•бога" "<a•href=/type/index_type_1-1.shtml>Роман</a>:" s~sks 
# gHit: id_P hitP numP new
# gSta: id_P txtL txtG id_T hitP numP new
# gTtl: id_P datP sizK sizN "namP" "moto"
# gTxt: id_P id_T sizT endD "namT" "jnrT"

}

{
  if(FNR==1){
    if(NR!=1)chkSteps()                           #  чем закончилась обработка предыдущего файла?
    step=0        # 0-владелец, 1-дата обновления, 2-id раздела, 3-разбор строк таблиц статистики
    buf=""
    fPth=FILENAME
    fNam=split(fPth,arr0,"\\")                    # NEW/NEXT входной файл as-is (с полным путём)
    fNam=split(arr0[fNam],arr0,".")               # файл: чистое nam.ext (like "0-0.stat.htm")
    fNam=arr0[1]                                  # fNam чистое имя (like "0-0" in "0-0.stat.htm")
    id_P=substr(fNam,1,1)"/"substr(fNam,3)
    if(!(id_P in aIdP)){                          #  это левый раздел (нет во вхолном списке)
      errS="] -  NO in gAuthH: "id_P" " fNam
      nextfile                                    #  что-то не так с датой обновления
    }
    int8++
    if(int8%100==0) print(sprintf(": %3s.%-3s",int8/100,int7),strftime("%H:%M"))
    else if(int8%10)printf(".")
    else            printf(":")                   #  оживляж...
  }
  gsub(/(&nbsp;)+|\xA0+|[\0-\31]+/," ",$0) #  цепочки "&nbsp;" и прочих "пробелов" -> " " (пробел)
  buf=buf" "$0
# gTtl: id_P sizK sizN datP "namP" "moto"
  if(step==0){                                   #  ищем/проверяем ФИО автора
# <h3>Фонд А.:<br> <font color="#cc5555">всяко-разное</font></h3>
    if(match(buf,/<h3>([^<]+):<br> <[^>]+>([^<]+)<\/font><\/h3>/,aj)){
      namo=pack(aj[1])" "pack(aj[2])        #  "namP" "moto"
      buf=substr(buf,RSTART+RLENGTH)
      step=1
      next
    }
  }else if(step==1){                                   #  ищем/проверяем ФИО автора
#  <li><b><a href=/long.shtml><font color=#393939>Обновлялось:</font></a></b> 28/03/2022
#  <li><b><a href=/rating/size/><font color=#393939>Объем:</font></a></b> 311k/1
    if(match(buf,/>Обновлялось:<.+([0-9]{2})\/([0-9]{2})\/(20[0-9]{2}).+>Объем:<.+ ([0-9]+)k\/([0-9]+) +</,aj)){
      aIdI[++nIdI]=aj[3]"."aj[2]"."aj[1] sprintf(" %7s %5s",aj[4],aj[5]) bRd id_P bRd aIdT[id_P] bRd namo
      buf=substr(buf,RSTART+RLENGTH)
      step=2
      nextfile
    }  
  }
}

function pack(f1s1) {       #  мешающие chars -> нейтральные + cut too long + закавычить результат
  f1n1=split(f1s1,f1a1)
  f1s1=f1a1[1]                                    #  обихаживание пробелов... " " -> "•"
  for(f1n2=2;f1n2<=f1n1;f1n2++)f1s1=f1s1"•"f1a1[f1n2]
  f1s2=""                                         #  двойные кавычки -> "&quot;"
  while(f1n1=index(f1s1,"\"")){f1s2=f1s2 substr(f1s1,1,f1n1-1)"&quot;"; f1s1=substr(f1s1,f1n1+1)}
  f1s1=f1s2 f1s1
  f1s2=""                                         #  "%" -> "&#x25;"
  while(f1n1=index(f1s1,"%")){f1s2=f1s2 substr(f1s1,1,f1n1-1)"&#x25;"; f1s1=substr(f1s1,f1n1+1)}
  f1s1=f1s2 f1s1
  if(length(f1s1)>999){                           #  обрезка слишком длинных литералов
    f1n1=index(substr(f1s1,1000),"•")
    if(!f1n1)f1n1=1000
    else   f1n1=f1n1+999                          #  v-вместо обрезанного - справка о факте ;)
    f1s1=substr(f1s1,1,f1n1-1)"•(+"(length(f1s1)-f1n1)" chars)"
  }
  return ("\""f1s1"\"")                           #  результат "закавычивается!"
}

function chkSteps() {                     #  проверка чем закончилась обработка очередного файла?
  if((step==2)&&(errS=="")){                      #  всё штатно ("ОК")
    return
  }else{
    if(errS==""){
      if(step==1){
        errS="] no or bad datP/sizK for "id_P" in: " fNam
      }else if(step==0){
        errS="] no or bad namP/moto for "id_P" in: " fNam
      }
    }
    aErr[++nErr]=errS
    errS=""                                       #  и очистим флаг-инфо
  }
}

END{
  if(ARGV[1]==""){
    print("] no_file_in")
    exit 1                                        # защита от случайного пуска
  }
  chkSteps()
  if(int8%100)print("")
  print(" == "int8":titls",strftime("%H:%M:%S",systime()-tIni,-1))  #  проблем нет
# print nIdI"~"aIdI[nIdI]"~"length(aIdI)
  asort(aIdI)
  for(jj=1;jj<=nIdI;jj++)         print(aIdI[jj]) >fOut
#   for(jj=1;jj<=nIdI;jj++)         print(jj" * "aIdI[jj])
  print(";  datP       sizK  sizN ! id_P ! id_T \"namT\" ! \"namP\" \"moto\"") >fOut
  for(jj=1;jj<=nEnd;jj++)print(aEnd[jj])          >fOut #  <- старый подвал + свежак -v
  j0=";5I titl "datJ":datJ "gapD":gapD "nIdI":nTit "nErr":nErr "strftime("%Y.%m.%d:done at~%H:%M")
  print(j0) >fOut
  if(nErr){
    for(jj=1;jj<=nErr;jj++)
    print(aErr[jj]) >fOut".err"
    nErr=nErr":nErr"
  }else nErr=""
  print(" ==",nIdI":nTit",nErr)
}

    Имя файла скрипта: "gAuthS.awk" V-текст скрипта-V
- - - - - - - - -
BEGIN{                      #  gAuthS.awk выченение хитов разделов/тектов за заданный интервал дат
# отбираются тексты с 200+ удачи /по одному самому удачному на раздел/

# 2023.01.25, nvv, сменилось разрешение экрана, слегка меняем оживляж
# ! прочая история в подвале
  if(ARGV[1]=="")exit                             #  защита от случайного пуска
  bRd=":!:"                                       #  разделитель блоков в строке
# массив имён месяцев для преобразования их в числа с лидирующим нулём 01..12
  mNum["Jan"]="01"                                #  .1 January . Jan. 31 . winter wint
  mNum["Feb"]="02"                                #  .2 February .Feb. 28/29
  mNum["Mar"]="03"                                #  .3 March . . Mar. 31 . spring sprn
  mNum["Apr"]="04"                                #  .4 April . . Apr. 30
  mNum["May"]="05"                                #  .5 May . . . May. 31
  mNum["Jun"]="06"                                #  .6 June . . .Jun. 30 . summer summ
  mNum["Jul"]="07"                                #  .7 July . . .Jul. 31
  mNum["Aug"]="08"                                #  .8 August . .Aug. 31
  mNum["Sep"]="09"                                #  .9 September Sep. 30 . autumn autm
  mNum["Oct"]="10"                                #  10 October . Oct. 31
  mNum["Nov"]="11"                                #  11 November .Nov. 30
  mNum["Dec"]="12"                                #  12 December .Dec. 31 . winter wint
  split("",aSel)    #  в ячейках статистики: url,title,hits:all,12mon,mon0..monC-11,day0..day0-61
  for(str2 in ENVIRON){                     #  ! регистр букв в имени переменной может быть любой
    str1=tolower(str2)                            #  Temp и temp - РАЗНЫЕ имена для gAwk...
    if(str1=="gdif") gDif=ENVIRON[str2]           #  исходный список
    if(str1=="minl") minL=ENVIRON[str2]           #  минимальная удача*10 текста "начерно"
  }
  if((gDif=="")||(minL=="")){                     #  вызвали НЕ из CMD
    gDif="U:\\Jobs\\#gAuth220325\\2022.03.22.21.gAuthH"
    gDif="\\Jobs\\#gAuth\\2022.03.22.21.gAuthH"
    gDif="2022.03.22.21.gAuthH"
    minL=100                                    #  мин. кол-во хитов в день (в среднем) "начерно"
  }
  j0=split(gDif,aj,"\\")
  fOut=aj[j0]
  if(j0==1)fPth=""
  else{
    fPth=aj[1]"\\"
    for(j1=2;j1<j0;j1++)fPth=fPth aj[j1]"\\"
  }
  split(fOut,aj,".")
  datJ=aj[1]"."aj[2]"."aj[3]
  gapD=aj[4]
  fOut=fPth datJ"."gapD".gAuthS"
# print(">"fOut"<")
# exit

  minT=minL*2                                     #  200 минимальная удача*10 текста "набело"
  preS=strftime("%Y.%m.%d",mktime(substr(datJ,1,4)" "substr(datJ,6,2)" "substr(datJ,9,2)" 12 00 00")-86400*(gapD-1))
#  формат входного списка
#    2456:!:0     53231  38     312         8:!:f/fox:!:"Fox" "Раздел..."
# ;Luck.1 ! new hitP.3 numP sizK.5 sizN ! id_P.7 ! "namP" "moto".9
#  block.1  <-  block.2 {aIdP[id_P]} ->   block.3  <-block.4{aIdD[id_P]} - ->
  while((getline line<gDif)>0){         #  ищем последнюю запись успешного скачивания этого списка
    split(line,aj,bRd)                            #  !!! причесать по новому!
    if(match(aj[1],/^ *[0-9]+$/)){                #  строка раздела
      id_P=aj[3]                                  #  его ID
      aIdP[id_P]=aj[2]                            #  его данные из aHitc
      aIdD[id_P]=aj[4]                            #  его описаните оттуда же
      nIdP++                                      #  счётчик разделов-кандидатов в рейтинг
    }else{                                        #  строка подвала
      if(match(aj[1],/^;[0-9][^ ]/))aEnd[++nEnd]=line #  прикопаем...
    }
  }
  close(gDif)
  int7=(nIdP-(nIdP%100))/100
  if(nIdP%100)int7=int7"+"
  txtD=0                                          #  число текстов с интервалом МЕНЬШЕ заданного
  errS=""
  nErr=0
  tIni=systime()
  print("scan "nIdP" stats within: "preS"-"datJ)
}

{                                                 #  // -=- основной блок скрипта begs
  if(FNR==1){
    if(NR!=1)chkSteps()                           #  чем закончилась обработка предыдущего файла?
    step=0        #  0-владелец, 1-дата обновления, 2-id раздела, 3-разбор строк таблиц статистики
    fPth=FILENAME
    fNam=split(fPth,arr0,"\\")                    #  NEW/NEXT входной файл as-is (с полным путём)
    fNam=split(arr0[fNam],arr0,".")               #  файл: чистое nam.ext (like "0-0.stat.htm")
    fNam=arr0[1]                                #  fNam чистое имя (like "0-0" in "0-0.stat.htm")
    id_P=substr(fNam,1,1)"/"substr(fNam,3)
    if(!(id_P in aIdP)){                          #  это левый раздел (нет во вхолном списке)
      errS=  "] -  NO in gAuthH: "id_P" " fNam
      nextfile                                    #  что-то не так с датой обновления
    }
    maxL=0                                        #  максимальная удача текста в разделе
    nTxt=0                                        #  кол-во отобранных текстов
    dayB=0    #  верхний день нашего интервала в таблицах статистики, значение: в диапазоне 17-77
    dayE=0                                      #  нижний день нашего интервала, НЕ входит в него
    maxT=0                                        #  хиты лидера (литеров)
    inTab=0                                       #  сначала - мы НЕ "в таблице"
    numT=0                                        #  счётчик таблиц в файле статистики
    numR=0                                        #  счётчик строк в текущей таблице
    selN=0
    int8++
    if(int8%100==0) print(sprintf(": %3s.%-3s",int8/100,int7),strftime("%H:%M"))
    else if(int8%10)printf(".")
    else            printf(":")                   #  оживляж...
  }
  gsub(/(&nbsp;)+|\xA0+|[\0-\31]+/," ",$0) #  цепочки "&nbsp;" и прочих "пробелов" -> " " (пробел)
  if(step==0) {                                   #  ищем/проверяем ФИО автора
# <h3>Статистика раздела &quot;<a href=./>Влад</a>&quot;:</h3>
    if (match($0,/<h3>Статистика раздела \&quot;<a href=\.\/>(.*)<\/a>\&quot;:<\/h3>/,arr0)) {
      if(arr0[1]==""){
        errS="]  NO nick in: " fNam
        nextfile                                  #  что-то не так с датой обновления
      }
#       namP=pack(arr0[1])
      step++                                      #  проверка ОК
    }
  }else if(step==1){                              #  вычленеие даты обновления статистики
    if(match($0,\
/<li><i>Статистика рассчитывается .+\. \(.+ (...) +([0-9]+) (..:..:..) (20[0-9][0-9])\)<\/i>/\
,arr0)){
      if(length(arr0[2])==1) arr0[2]="0"arr0[2]   #  причешем первые дни месяца "1" -> "01"
      ticS=mktime(arr0[4]" "mNum[arr0[1]]" "arr0[2]" 12 00 00") # тики на 12 часов даты статистики
      datS=arr0[4]"."mNum[arr0[1]]"."arr0[2]      #  сама дата пополнения статистики
      timS=arr0[3]                                #  время оного-же
      step++                                      #  дату статистики оптичили
    }
  }else if(step==2){                        #  ищем authID (!должен быть) в отсылке на top.mail.ru
# [<a href=http://top.mail.ru/pages?id=77427&period=0&date=2018-10-02&filter_type=0
# &filter=n/nosow_w_w&pp=20&gender=0&agegroup=0>по дням</a>]
    if (match($0,/\[<a href=http.+&filter=([0-9a-z]\/[\-0-9a-z_]+)&pp=.+>по дням<\/a>]/,arr0)) {
      if(id_P!=arr0[1]){                          #  это ПОЛНЫЙ криминал!
        errS="]  "arr0[1]" != fNam: " fNam
        nextfile
      }
      step++                                      #  проверка ОК
    }
  }else if(step==3){                #  таблицы статистики, просмотр строк, сборка данных в ячейках
    if($0=="<!--------- Подножие ------------------------------->"){  #  разбор закончен
      step++
      nextfile
    }
    if(inTab){                      #  Мы ВНУТРИ таблицы, собираем строки таблицы с хитами текстов
      buf0=buf0" "$0                              #  gain inpLines to buf0, then check contents
      while((match(buf0,/<tr ?[^>]*>/))||(int0=index(buf0,"</tr"))){ # seek <tr .. </tr for tabRow
        if(RSTART){                               #  new/next line begs
          numR++
          buf0=substr(buf0,RSTART+RLENGTH)        #  cut <tr ?*> at the beginning of the line
        }
        if(int0=index(buf0,"</tr")){              #  this line ends
          getSels(substr(buf0,1,int0-1))          #  tabl_row -> arrow:sell_values
          buf0=substr(buf0,int0+4)              #  убрали эту строку из буфера и окультуриваем...
          sErr=""                                 #  пока этот текст/раздел НЕ в браке
          id_T=aSel[0]                        #  имя+расшир файла с текстом (или "./" для раздела)
          namT=aSel[1]                    #  пока фактическое (as-is) название текста в статистике
          if((numT==1)&&(numR==2)){               #  хиты по разделу (спец.обслуживание)
#             namT="\"<!>\""
#             id_T="~"                              # prt 8-раздел: 'хиты за 30 дней'
# 0.1-имя/ссылка; хиты: 2-всего, 3-12мес, 4..15-заМес, 16..77-заСутки
            if(minT-luck>0){                      #  у раздела мало хитов за нужный интервал
#               errS="] step."step" (luck:"luck"; less:"minT" in fNam: " fNam " )"
              step=4
              nextfile
            }else{
              aIdL[id_P]=luck                     #  реальная удача раздела
            }
          }else{                                  #  обычная строка статистики текста
            if(minT-aSel[3]>0){                   #  дальше только неудачники
              step=4
              nextfile
            }
            int0=split(id_T,arr0,".")
            id_T=arr0[1]
            if(int0!=2){
              sErr="zOrg"                         #  нарушен формат имени файла: name.ext
            }else if(arr0[2]!="shtml"){
              sErr="xExt"                         #  расширенеие не то-с
            }else if (gensub(/[\-0-9a-z_]/,"","g",arr0[1])!=""){
              sErr="vNam"                         #  левые символы в имени файла текста
            }else if(namT==""){
              sErr="tHdr"                         #  "пустое" название текста
            }else if(minT-luck>0){                   
              sErr="lowH"                         #  у текста мало хитов за нужный интервал
            }else{
              if(maxL-luck<0){                    #  отбираем самый успешный текст
                maxL=luck
                if(nulN!=0){
                  nulN=gapD-nulN
                  txtD++
                }
                aTxL[id_P]=sprintf("%6.0f %2s ",luck,nulN)  #  удача текста за... это цифры
                aTxN[id_P]=id_T" "pack(namT)      #  и его атрибуты это цепочки символов
              }
            }
            sErr=""
          }
        }
      }
      if(int0=index(buf0,"</table")){             #  this tabl ends
        buf0=substr(buf0,int0+4)                  #  update buf0
        inTab=0
      }
    }else{                                        #  ищем начало таблицы
      if(int0=index($0\
,"<table border=1 cellspacing=0 cellpadding=0><tr><td><td colspan=2><b>Итого</b></td>")){
        numT++                                    #  new table
        numR=1                #  певая строка каждой таблицы - календарь хитов статистики раздела
        buf0=substr($0,int0+80)
        if(numT==1){                              #  1-st table 1-st row -> get col-dates & so-on
          match(buf0,/<tr[^>]*>(.+)<\/tr[^>]*>/,arr0) #  собственно строка с календарём
          getSels(arr0[1])                        #  но самую первую такую - разберём-с
          tic0=ticS           #  и привяжем дату к нулевому (самому левому) дню посуточных данных
          int0=16
          while(strftime("%d",tic0)!=aSel[int0])tic0=tic0-86400 # сдвинем стат.дату к нулевому дню
          while(int0<78){
            int0++
            tic0=tic0-86400
            datS=strftime("%Y.%m.%d",tic0)
            if(datS==datJ)dayB=int0
            if(datS==preS)dayE=int0
          }
          if(!dayB){
            errS="]  NO datJ:"datJ", in " fNam
            nextfile
          }
          if(!dayE){
            errS="]  NO preS:"preS", in " fNam
            nextfile
          }
        }
        inTab=1                                   #  мы ВНУТРИ таблицы
        buf0=""                                   #  буфер чист!
      }
    }
  }
}                                                 #  // -=- основной блок скрипта ends

function getSels(rowLn){                #  разделение строки таблицы статистики на массив значений
  hitT=0                                          #  + сумма хитов "за 30 дней"
  selN=0                                          #  sells count
  nulN=0                                          #  число пустых ячеек в нашем интервале
  split("",aSel)
  aSel[0]=""
  while(match(rowLn,/<td[^>]*>/)){                #  это объявление ячейки
    selN++                  #  0.1-имя/ссылка; хиты: 2-всего, 3-12мес, 4..15-заМес, 16..77-заСутки
    rowLn=substr(rowLn,RSTART+RLENGTH)            #  выкусим объявление ячейки
    f3n2=index(rowLn,"</td")                      #  это конец ячейки
    f3s2=substr(rowLn,1,f3n2-1)               #  это сама ячейка (с возможными внутренними тегами)
    if(selN==1){                            #  в ПЕРВОЙ ячейке - название текста и ссылка на него
      match(f3s2,/<a href=([^>]+)>(.*)<\/a>/,f3a1)  #  выцепим и разложим:
      aSel[1]=f3a1[2]                             #  название текста
      aSel[0]=f3a1[1]                             #  ссылка на текст: name.shtml
    }else aSel[selN]=rmTags(f3s2) #  в ПРОЧИХ ячейках - только значение ячейки, очищенное от тегов
    rowLn=substr(rowLn,f3n2)                      #  выкусим оптиченную ячейку
    if(selN-dayB>=0){                             #  начало /самый свежий день/ нашего интервала
      if(dayE-selN>=0){                           #  внутри этого интервала
        hitT=hitT+aSel[selN]                      #  считаем сумму хитов за gapD
        if(aSel[selN]==0)nulN++                   #  пустая ячейка В интервале(!), зачтём
      }
      if(aSel[selN]!=0)nulN=0                     #  непустая ячейка ПОСЛЕ начала интервала
    }
  }
  if(gapD==nulN)luck=0                            #  удача этой строки статистики
  else          luck=sprintf("%5.0f",10*hitT/(gapD-nulN))+0
#   print numT,numR,luck
  if(selN==77)return                              #  row HAVE had 77 sells
  nextfile                                        #  битая таблица == битый файл
}

function rmTags(f2s1){                            #  удаление ВСЕХ тегов в литерале
  while(match(f2s1,/<\/?[a-zA-Z][1-6a-zA-Z]?[a-zA-Z]*[ \/]?[^>]*>/))
      f2s1=substr(f2s1,1,RSTART-1) substr(f2s1,RSTART+RLENGTH)
  return(f2s1)
}

function pack(f1s1) {       #  мешающие chars -> нейтральные + cut too long + закавычить результат
  f1n1=split(f1s1,f1a1)
  f1s1=f1a1[1]                                    #  обихаживание пробелов... " " -> "•"
  for(f1n2=2;f1n2<=f1n1;f1n2++)f1s1=f1s1"•"f1a1[f1n2]
  f1s2=""                                         #  двойные кавычки -> "&quot;"
  while(f1n1=index(f1s1,"\"")){f1s2=f1s2 substr(f1s1,1,f1n1-1)"&quot;"; f1s1=substr(f1s1,f1n1+1)}
  f1s1=f1s2 f1s1
  f1s2=""                                         #  "%" -> "&#x25;"
  while(f1n1=index(f1s1,"%")){f1s2=f1s2 substr(f1s1,1,f1n1-1)"&#x25;"; f1s1=substr(f1s1,f1n1+1)}
  f1s1=f1s2 f1s1
  if(length(f1s1)>999){                           #  обрезка слишком длинных литералов
    f1n1=index(substr(f1s1,1000),"•")
    if(!f1n1)f1n1=1000
    else   f1n1=f1n1+999                          #  v-вместо обрезанного - справка о факте ;)
    f1s1=substr(f1s1,1,f1n1-1)"•(+"(length(f1s1)-f1n1)" chars)"
  }
  return ("\""f1s1"\"")                           #  результат "закавычивается!"
}

function chkSteps() {                     #  проверка чем закончилась обработка очередного файла?
  if((step==4)&&(errS=="")){                      #  всё штатно ("ОК")
#     if(maxL)aIdP[id_P]=aIdP[id_P]" "aTxL[id_P]    #  в разделе есть годный текст
    return
  }else{
    if(errS==""){                                 #  что-то не так, но нужно уточнить...
      if(step==3){
        if(inTab==1){                             #  уточним проблему...
          if(selN!=77){                           #  row HAVE had 77 sells
        errS="] step."step" (selN:"selN" Tab:"numT" Row:"numR"{not 77}): " fNam " )"
          }else if((numT==1)&&(numR==2)){
        errS="] step."step" (bad 1-st table 2-nd row in: "    fNam " )"
          }else{
        errS="] step."step" (unClosed table in: "             fNam " )"
          }
        }else{
        errS="] step."step" (no or bad table in: "            fNam " )"
        }
      }else if(step==2){
        errS="] step."step" (no or bad author's ID in: "      fNam " )"
      }else if(step==1){
        errS="] step."step" (no or bad refresh date:{"$0"}: " fNam " )"
      }else if(step==0){
        errS="] step."step" (no partition`s owner in: "       fNam " )"
      }
    }
    aErr[++nErr]=errS
    errS=""                                       #  и очистим флаг-инфо
  }
}

END{                                              #  отстрелялись; ну почти...
  if(ARGV[1]==""){
    print("? NO file in")
    exit 1                                        #  защита от случайного пуска
  }
#   exit
  chkSteps()                                      #  чем закончилась обработка последнего файла?
  if(int8%100)print("")
  print(" == "int8":stats",strftime("%H:%M:%S",systime()-tIni,-1))  #  проблем нет
# aTxt == 10*Luck, id_P, id_T, namP, namT, fNam
# (! в массиве наверняка есть "лишние" тексты из-за алгоритма: aHit[int0] < maxT )
# id_P=aj[3] # ID раздела
# aIdL[id_P] # его реальная удача
# aIdP[id_P] # его данные из aHitc
# aTxL[id_P] # его удача текста за...
# aTxN[id_P] # и атрибуты его текста
# aIdD[id_P] # его описание оттуда же
#  для правильной сортировки по успеху именно текста припишем его спереди
  for(jj in aTxL)aOut[++nOut]=aTxL[jj] sprintf("%5s %s",aIdL[jj],aIdP[jj] bRd jj bRd aTxN[jj] bRd aIdD[jj])
#  формат вЫходного списка
#   613 0  65951     29    1159     3     613   0:!:k/kadawr:!:n "Псион":!:"Кадавр" "no•comments"
# ;Luck new.2 hitP numP.4 sizK sizN.6 txtL txtG.8 ! id_P ! id_T.10 "namT" ! "namP".12 "moto"
# <- - - - - - - block.1 - - - - - - - - - - - ->  block.2 <- block.3 ->    <- block.4 ->
  asort(aOut)
  for(jj=nOut;jj>0;jj--)print(aOut[jj]) >fOut     #  при выдаче приписанное надоть изъять!
print(";txtL txtG.2 Luck new.4 hitP numP.6 sizK sizN.8 ! id_P ! id_T.10 \"namT\" ! \"namP\".12 \"moto\"")>fOut
  for(jj=1;jj<=nEnd;jj++)print(aEnd[jj]) >fOut    #  <- старый подвал + свежак -v
j0=";5H txL1 "datJ":datJ "gapD":gapD "nOut":nTxL "nErr":nErr "minT":minT "txtD":txtD "strftime("%Y.%m.%d:done at~%H:%M")
  print(j0) >fOut
  if(nErr){
    asort(aErr)
    for(int0=1;int0<=nErr;int0++)print(aErr[int0])>fOut".err"
    print(j0)                                     >fOut".err"
    nErr=nErr":nErr"
  }else nErr=""
  print(" ==",nOut":nTxL",nErr)
# 2020.11.23, nvv, creation
# 2021.01.11, nvv, it works ok...
# 2021.05.02, nvv, мелкая косметика
# 2022.04.18, nvv, радикально переделано - теперь выдача точней и структурирована в блоки
}

    Имя файла скрипта: "iNetR.awk" V-текст скрипта-V
- - - - - - - - -
BEGIN{                                  #  iNetR.awk - оформление в HTML приложения "siTopAuthors"
  if(!(1 in ARGV))exit                             # защита от случайного пуска

  minR=333            # нижняя граница отбора в наш список: 33.3 посетителя в день, 999 за 30 дней
  minR=200

# 2023.08.02, nvv, убран анонс скрытых разделов
#  ! прочая история - в подвале

  rTyp="<b>si&#x54;op&#x41;uthors</b>"              # siTopAuthors
  ancW="t"                                # общий признак типа рейтинга в якорях, комментах и т.п.
  ancS=":"                                  # частный признак якоря места (в формате: "ггммвв:NNN"
  flag=0                                          # флаг для чередующейся раскраски строк таблиц
  tGap=50                                         # строк в одной таблице
  tMax=1000                                       # строк в списке всего
  oldP=0                                          # (раздел)размещено более года назад
  curP=0                                          # (-"-) - "" - в границах интервала и позже
  oldT=0                                          # (текст)размещено более года назад
  curT=0                                          # (-"-) - "" - в границах интервала и позже
  txtM=0                        # число разделов с более чем одним лидером (с одинаковым успехом)
  auUp=0                                          # раздел, сдвиг вверх
  auDn=0                                          # раздел, сдвиг вниз
  auNw=0                                          # новый автор
  upAu=0                                          # объём+ раздела
  dnAu=0                                          # объём- раздела
  nwTx=0                                          # новый текст
  upTx=0                                          # объём+ текста
  dnTx=0                                          # объём- текста
  pDat=-1                                         #  дата aHits по СИ
  preH=-1                                         #  дата "скачан" (aHits)
  pPag=-1                                         #  страниц aHits
  hDat=-1                                         #  дата aHits по СИ
  donH=-1                                         #  дата "скачан" (aHits)
  hPag=-1                                         #  страниц aHits
  hMax=-1                                         #  хиты раздела-лидера aHits
  minL=-1                                         #  минимальная "удача" раздела
  hold=-1                                         #  дата "расчёт"
# "/" == общий префикс урла СИ: samlib.ru
  nUrl="n/nosow_w_w"                              # nvv-с...
#  jDat=strftime("%y%m%d%H%M%S",systime())         # дата для якорей таблиц

  for ( jj in ENVIRON ) {                     # ! регистр букв в имени переменной может быть любой
    j1=tolower(jj)
    if ( j1 == "fold" ) fOld=ENVIRON[jj]          # ИМЯ опорного файла (like 2018.11.29.mText)
    if ( j1 == "hdir" ) hDir=ENVIRON[jj]"\\"      #  место для файлов *.HTML
    if ( j1 == "minr" ) minR=ENVIRON[jj]          #  минимальный успех*10 текста-лидера в разделе
    if ( j1 == "minl" ) minL=ENVIRON[jj]          #  минимальный успех*10 раздела
  }
  if(fOld==""){
    fOld="U:\\Stor\\Text\\zOld.gAuths\\2021.05.26.3.gAuth"
    minL=100
  }

  nMth[ 1]="января"
  nMth[ 2]="февраля"
  nMth[ 3]="марта"
  nMth[ 4]="апреля"
  nMth[ 5]="мая"
  nMth[ 6]="июня"
  nMth[ 7]="июля"
  nMth[ 8]="августа"
  nMth[ 9]="сентября"
  nMth[10]="октября"
  nMth[11]="ноября"
  nMth[12]="декабря"

   color[0]="\"#FFFFE0\""                     # (четная строка таблицы) светло-жёлтый line color 0
   color[1]="\"#F5F5DC\""                   # (НЕчетная строка таблицы) грязно-жёлтый line color 1
   color[2]="\"#C0C0C0\""                         # gray серый (всё плохо)
   color[3]="\"#FFF4E0\""                         # светло-розовый (ниже, меньше, плохо, нет)
   color[4]="\"#F0FFE0\""                         # светло-зелёный (выше, больше, хорошо)
   color[5]="\"#FFFFB0\""                         # светло-жёлтый - новое
   color[6]="\"#FFFF70\""                       # yellow желтый (нечётные ячейки в шапках таблиц)
   color[7]="\"#F8F8F8\""                         # светло-серый - наш рейтинг
   color[8]="\"#0000FF\""                         # синий (птичка nvv)
   color[9]="\"#000000\""                         # чёрный (вратый объём)
  color[10]="\"#E8F8B8\""                         # зелёный - ячейки с текстом нашего рейтинга
  color[11]="\"#E0F8FF\""                 # светло-сиреневый - размещено "давно" больше года назад
  color[12]="\"#FFD700\""                         #  оранжевый "потеряшки" /скрытые разделы/ aad2
# jDir datJ"."gapD"."nJob == U:\Jobs\2020.11.14.3.gGapR
  jr=split(ARGV[1],aq,"\\")                       # "U:" + "Jobs" + "2020.11.14.3.gGapR"
  split(aq[jr],aq,".")                            # "2020.11.14.3.gGapR"
  rYea=aq[1]                                      #  год рейтинга !привязан к верхней дате выборки
  rMth=aq[2]                                      #  месяц его же
  rDay=aq[3]                                      #  день его же
  datJ=rYea"."rMth"."rDay                         #  дата верхнего конца интервала (включая)
  gapD=aq[4]                                      #  интервал (в днях) обсчитанного периода
  fOut=hDir datJ"."gapD".gAuth.html"       #  "U:\Stor\Html\" + "2020.11.14" + ".3" + ".auth.html"
  fPre=hDir datJ"."gapD".gAuth.z.html"  #  "U:\Stor\Html\" + "2020.11.14" + ".3" + ".auth.z.html"

  dBeg=strftime("%Y.%m.%d",mktime(rYea" "rMth" "rDay" 12 00 00")-86400*(gapD-1)) # дата 1-го дня выборки
  yBeg=substr(dBeg,1,4)                           # год этого 1-го дня интервала нашей выборки
  oldD=rYea-1"."rMth"."rDay                       # "год назад" - привязка к КОНЦУ нашей выборки
  tOld="года"
  qGap=gapD"_дн."
  if(gapD==1)       sGap="день"
  else if(gapD-5<0) sGap="<b>"gapD"</b> дня"
  else              sGap="<b>"gapD"</b> дней"
  info="<!-- (L) nvv; type: 'Auth' \"curr siTopAuthors\" "strftime("%Y.%m.%d %H:%M")" -->" #  mark
  inf2="<!-- (L) nvv; type: 'Auth' \"prev siTopAuthors\" "strftime("%Y.%m.%d %H:%M")" -->" #  mark
  print "--- " strftime("%H:%M:%S") " Begs..."  # начали работу (оживляж;)
# подвязка позиций прошлого рейтинга, для отметок "сдвиг" (места)
#  $1  $2   $3   $4   $5   $6   $7   $8   $9       $10        $11    $12  $13    $14    $15   $16    $17   $18
# Luck new  hitP numP sizK sizN hitT txtN sizT     datP       endD   id_P "namP" "moto" id_T "namT" "jnrT" fNam
# 5527 0  218357 13   936  6    4937  1    7   2020.11.15 2020.08.31 z/zhan "Tony" "kjujdj•xbnfntkz" flibustain "Известные•мне•зеркала•Флибусты•и•Либрусека" "<a•href=/type/index_type_5-1.shtml>Статья</a>:" z~zhan
# 10330 0 206377 14   938  8   10333  1    7.1 2021.04.03 2020.08.31 z/zhan "Tony" "kjujdj•xbnfntkz" flibustain "Известные•мне•зеркала•Флибусты•и•Либрусека" "<a•href=/type/index_type_5-1.shtml>Статья</a>:" z~zhan 

  if(fOld!=""){
    tmp0=0
    while ((getline line < fOld) > 0) {             # fOld: like 2018.11.29.mText
      split(line,aj)
      if(match(aj[1],/^[\.0-9]+$/)){              # это именно строка с успехом раздела &Co
        if((aj[7]-minR>=0)&&(aj[1]-minL>=0)){     #  успех текста/раздела МОЖЕТ быть ниже границ
          tmp0++                                  #  старая позиция раздела в старом списке
          a0a[aj[12]]=tmp0                        #  место раздела в старом списке
          a0k[aj[12]]=aj[5]                       #  объём раздела в кбайтах
          a0n[aj[12]]=aj[6]                       #  наполнение в штуках ;)
          a0t[aj[12]]=aj[15]                      #  id_T (текста-лидера)
          split(aj[9],aq,".")
          a0z[aj[12]]=aq[1]                       #  объём текста
        }
      }else if(match(line,/^;5H .+ 20([0-9]+)\.([0-9]+)\.([0-9]+):done /,aj))oDat=aj[1] aj[2] aj[3]
    }
  }
  close(fOld)                                     # всё/не_всё, - старый список тогось
  jj=0
print "fOld: "fOld
print "fLst: "ARGV[1]
print "fOut: "fOut
print "dBeg:"dBeg,"gapD:"gapD,"datJ:"datJ,"oDat:"oDat
# exit
}

function mkPage() {               # сборка всей страницы рейтинга/приложения (тип, строк, куда-с)
  if(strftime("%j")-355>0)jj="С наступающим"
  else if(strftime("%j")-11<0)jj="С"
  else jj=""
  if(jj!=""){
    aHnY[++nHnY]="<table align=center border=0 cellspacing=0 cellpadding=2>"
    aHnY[++nHnY]="<tr align=center bgcolor=navy><td><font size=+2 color=aqua>"
    aHnY[++nHnY]="<b>&nbsp;&#x2744; "jj" новым годом! <font color=white>&#x2744;"
    aHnY[++nHnY]="<font size=+2 color=lime>Happy New Year! &#x2744;&nbsp;</b></td>"
    aHnY[++nHnY]="</tr></table>"
    aHnY[++nHnY]="<hr>"
  }
  aHdr[++nHdr]="См. <i>также</i>:"
  aHdr[++nHdr]="<br>?"
  aHdr[++nHdr]="<a href=/"nUrl"/si_pop-brief.shtml>Как узнать<b>"
  aHdr[++nHdr]="популярность произведения</b> в \"Самиздате\"</a>"
  aHdr[++nHdr]="(\"<b>где/что смотреть</b>\" - основное, вкратце)."
  aHdr[++nHdr]="<br>~"
  aHdr[++nHdr]="<a href=/"nUrl"/si_top500look-curr.shtml>Рейтинг"
  aHdr[++nHdr]="<b>siTopTexts</b> \"Произведения\"</a>"
  aHdr[++nHdr]="(\"Самиздат\", самое популярное за <b>30 дней</b>)."
# † массив строк шапки ТОЛЬКО у последнего рейтинга
  aHd1[++nHd1]="<br>#"
  aHd1[++nHd1]="<a href=/"nUrl"/sitopscriptslist.shtml>"
  aHd1[++nHd1]="Исходные <b>тексты скриптов</b> задач \"<b>siTop…</b>\"</a>"
  aHd1[++nHd1]="<br>&#x00B1;" # <font color=red size=-2>New</font>" 2022.12.01 off
  aHd1[++nHd1]="<a href=/n/nosow_w_w/si_votes-brief.shtml><b>Оценка произведений</b>"
  aHd1[++nHd1]="в \"Самиздате\"</a> (<b>что, где, как</b> и почему&hellip;)."
  aHd1[++nHd1]="<br>&#x221A;"
  aHd1[++nHd1]="<a href=/n/nosow_w_w/si-ini_reg.shtml><b>Регистрация</b> в \"Самиздате\" для получения"
  aHd1[++nHd1]="статуса &#171;<b>пользователь</b>&#187;</a> (оформление своего аккаунта)."
  aHd1[++nHd1]="<hr>"

  aHd1[++nHd1]="<b>NB.</b> <i>Названия</i> произведений и <i>псевдонимы</i>"
  aHd1[++nHd1]="авторов (как и прочие <i>сведения</i> в таблицах) приведены"
  aHd1[++nHd1]="<i>\"<a title=\"как есть\""
  aHd1[++nHd1]="href=\"https://ru.wikipedia.org/wiki/%D0%9A%D0%B0%D0%BA_%D0%B5%D1%81%D1%82%D1%8C\"><u>as"
  aHd1[++nHd1]="is</u></a>\", по состоянию на момент</i> сбора"
  aHd1[++nHd1]="данных. <i>Текущее</i> состояние можно узнать, пройдя"
  aHd1[++nHd1]="<i>по ссылкам</i> (в ячейках таблиц)."
  aHd1[++nHd1]="<br><b>(!)</b> <i><u>Если</u></i> на месте некоторых букв"
  aHd1[++nHd1]="(символов) в <i>названии</i> произведения или в <i>псевдониме</i>"
  aHd1[++nHd1]="автора вы видите \"<i><u>пустоту</u></i>\", то <i><u>у"
  aHd1[++nHd1]="вас</u></i>, скорей всего, <i><u>отсутствует</u></i>"
  aHd1[++nHd1]="шрифт с такими символами (иероглифы, руны, etc.)."
  aHd1[++nHd1]="<br>(?!) \"<b>Успех</b>\" - <i>среднее</i> число"
  aHd1[++nHd1]="посетителей за <b>сутки</b>. Причём /за период между"
  aHd1[++nHd1]="<i>обновлениями</i> учётных данных/ успех <b>раздела</b>"
  aHd1[++nHd1]="должен быть <i>не ниже</i> <b>"minL"</b> (отбор начерно),"
  aHd1[++nHd1]="а успех произведения-<b>лидера</b> (в нём) - <i>не"
  aHd1[++nHd1]="ниже</i> <b>"sprintf("%4.1f",minR/10)"</b> (отбор начисто)."
  aHd1[++nHd1]="<br><b>(#)</b>"                   #  2023.08.02
#   aHd1[++nHd1]="<font color=\"#DC143C\">NEW</font>" #  2023.06.23 crimson
#   aHd1[++nHd1]=""                          #  2023.07.08
  aHd1[++nHd1]="<font color=\"#A52A2A\"><b>Скрытые</b></font> авторы <u><i>отсутствуют</i></u>" # brown
  aHd1[++nHd1]="в \"<a href=/rating/hits/>Рейтинге по количеству посетителей</a>\""
#   aHd1[++nHd1]=""
#   aHd1[++nHd1]="<br><b>(!) <font color=broun>Изменён приоритет</font></b>: теперь"
#   aHd1[++nHd1]="<i>место раздела</i> определяется <i>самым успешным произведением</i> в нём."

# † массив строк шапки ТОЛЬКО у предпоследнего рейтинга

  if(!auUp)auUp=" --"                              # так красивше (чем унылый "0";)
  if(!curP)curP=" --"
  if(!upAu)upAu=" --"
  if(!curT)curT=" --"
  if(!upTx)upTx=" --"
  if(!auDn)auDn=" --"
  if(!oldP)oldP=" --"
  if(!dnAu)dnAu=" --"
  if(!oldT)oldT=" --"
  if(!dnTx)dnTx=" --"

# † массив строк шапки краткой справки последнего рейтинга
  aTb1[++nTb1]="<hr>"
  aTb1[++nTb1]=""
  aTb1[++nTb1]="<table align=center border=1 cellspacing=0 cellpadding=2>"
  aTb1[++nTb1]="<tr align=center>"
  aTb1[++nTb1]="<td bgcolor="color[1]" colspan=\"6\">Краткая сводка + цвет некоторых"
  aTb1[++nTb1]="ячеек таблиц (<i>предыдущий рейтинг -"
  aTb1[++nTb1]="<a href=/"nUrl"/si_top6author-prev.shtml><b>"oDat"</b></a></i>)</td></tr>"

# † массив строк шапки краткой справки ПРЕДпоследнего рейтинга
  aTb2[++nTb2]="<hr>"
  aTb2[++nTb2]=""
  aTb2[++nTb2]="<table align=center border=1 cellspacing=0 cellpadding=2>"
  aTb2[++nTb2]="<tr align=center>"
  aTb2[++nTb2]="<td bgcolor="color[1]" colspan=\"6\">Краткая сводка + цвет некоторых"
  aTb2[++nTb2]="ячеек таблиц (<i>предыдущий рейтинг - <b>"oDat"</b></i>, последующий"
  aTb2[++nTb2]="- <a href=/"nUrl"/si_top6author-curr.shtml><b>здесь</b></a>)</td></tr>"

# † массив строк тела краткой справки общий
  aTbl[++nTbl]="<tr align=center bgcolor="color[0]">"
  aTbl[++nTbl]="<td colspan=3><b>Авторы</b> (разделы)</td>"
  aTbl[++nTbl]="<td rowspan=\"5\"></td>"
  aTbl[++nTbl]="<td colspan=2><b>Произведения</b> (лидеры)</td></tr>"
  if(auNw+nwTx){
    aTbl[++nTbl]="<tr align=center>"
    if(auNw+length(a90)){                         #  если есть новое ИЛИ скрытое
      j0="<td bgcolor="color[12]">скрытые.<b>"length(a90)"</b></td>"
      j0=j0"<td bgcolor="color[5]" colspan=2>новые.<b>"auNw"</b></td>"  #  <td> </td>
    }else{
      j0="<td colspan=3></td>"                    #  иначе - пусто
    }
    aTbl[++nTbl]=j0
    if(nwTx)aTbl[++nTbl]="<td bgcolor="color[5]" colspan=2>новые.<b>"nwTx"</b></td></tr>"
    else    aTbl[++nTbl]="<td colspan=2></td></tr>"
  }
  aTbl[++nTbl]="<tr align=center bgcolor="color[1]">"
  aTbl[++nTbl]=                  "<td><b>N</b>(место), сдвиг</td>"
  aTbl[++nTbl]=                  "<td><b>Наполнение</b> стало</td>"
  aTbl[++nTbl]=                  "<td><b>Обновлялось</b></td>"
  aTbl[++nTbl]=                  "<td><b>Изменено</b></td>"
  aTbl[++nTbl]=                  "<td><b>Объём</b> стал</td>"
  aTbl[++nTbl]="</tr><tr align=center bgcolor="color[4]">"
  aTbl[++nTbl]=                  "<td>вверх.<b>"  auUp"</b></td>"
  aTbl[++nTbl]=                  "<td>больше.<b>" upAu"</b></td>"
  aTbl[++nTbl]=                  "<td>намедни.<b>"curP"</b></td>"
  aTbl[++nTbl]=                  "<td>намедни.<b>"curT"</b></td>"
  aTbl[++nTbl]=                  "<td>больше.<b>" upTx"</b></td>"
  aTbl[++nTbl]="</tr><tr align=center>"
  aTbl[++nTbl]="<td bgcolor="color[3]  ">вниз.<b>"auDn"</b></td>"
  aTbl[++nTbl]="<td bgcolor="color[3]">меньше.<b>"dnAu"</b></td>"
  aTbl[++nTbl]="<td bgcolor="color[11]">давно.<b>"oldP"</b></td>"
  aTbl[++nTbl]="<td bgcolor="color[11]">давно.<b>"oldT"</b></td>"
  aTbl[++nTbl]="<td bgcolor="color[3]">меньше.<b>"dnTx"</b></td>"
  aTbl[++nTbl]="</tr></table>"

  aTbl[++nTbl]="<table align=center border=0 cellspacing=0 cellpadding=2><tr>"
  aTbl[++nTbl]="<td>(</td>"
  aTbl[++nTbl]="<td bgcolor="color[11]">&nbsp;<b><i>давно</i></b>"
  aTbl[++nTbl]="– больше <b>"tOld"</b> назад&nbsp;</td>"
  aTbl[++nTbl]="<td>)</td>"
  aTbl[++nTbl]="</tr></table>"
  aTbl[++nTbl]="<hr>"
  aTbl[++nTbl]=""
  aTbl[++nTbl]="<font size=\"-2\" face=monospace color="color[8]">"
  aTbl[++nTbl]="&nbsp;&nbsp;/\\&nbsp;&nbsp;&nbsp;&nbsp;/\\&nbsp;&nbsp;<br>"
  aTbl[++nTbl]="&nbsp;·--·--·--·&nbsp;<br>"
  aTbl[++nTbl]="&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\\/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>"
  aTbl[++nTbl]="&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/\\&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>"
  aTbl[++nTbl]="&nbsp;&nbsp;&nbsp;&nbsp;·--·&nbsp;&nbsp;&nbsp;&nbsp;<br></font>"

# † титулы (названия) и рейтинга и его копии "Предыдущий"

  Ttl1="<center><font size=\"+2\">Рейтинг "rTyp"-"ancD"</font>"
  Ttl2="<center><font size=\"+2\">Предпоследний рейтинг "rTyp"-"ancD"</font>"

  aOut[++nOut]="<br>\"Самиздат\", самые посещаемые авторы"  #   (<i><b>намедни</b></i>)
  aOut[++nOut]="<table align=center border=0 cellspacing=0 cellpadding=2>"
  aOut[++nOut]="<tr bgcolor="color[4]">"  #  <td>(</td>
  if(gapD==1){
         sGap="день"
    aOut[++nOut]="<td>&nbsp;за "sGap": "dat_str(dBeg)" "yBeg" г.</td>"
  }else{
    j0="с "dat_str(dBeg)
    if(yBeg!=rYea)j0=j0" "yBeg" г."
    j0=j0" по "dat_str(datJ,1)"&nbsp;"
    aOut[++nOut]="<td>&nbsp;за "sGap": "j0"</td>"
  }
#  if(dOff!="0.0")aOut[++nOut]="<td bgcolor="color[3]">(<i><u>нет данных</u> за: "dOff"</i>)</td>"
  aOut[++nOut]="</tr></table>"
  aOut[++nOut]="(приложение к рейтингу <b>siTopTexts</b>-"ancD")"
  aOut[++nOut]="</center></blockquote>"
  bLin=mkCap()                                    # шапка - первая (нулевая-с) строка таблиц
  lMax=tMax                                       # всего строк в таблице
  for ( tIni=1; tIni<=lMax; tIni=tIni+tGap ) {
    aOut[++nOut]="<blockquote>"  # начало очередной таблицы
    aOut[++nOut]="<table align=center border=1 cellspacing=0 cellpadding=2>"  # навигация в
    aOut[++nOut]="<caption><a id=\""ancD ancW tIni"\"></a>"mkGoTo(tIni)"</caption>"  # заголовке
    if(tIni==1)aOut[++nOut]=mkCap0() # шапка "ini"
    aOut[++nOut]=bLin  # шапка таблицы
    tEnd=tIni+tGap
    for (tBeg=tIni;tBeg<tEnd;tBeg++) {
      if(tBeg<=lMax) mkTabLn(tBeg,tBeg)     # строка данных в таблице
    }
    if ((tBeg-tMax)>0) aOut[++nOut]=bLin
    aOut[++nOut]="</table>"  # таблица готова
    aOut[++nOut]="</blockquote>"  # закрытие блока
  }
  aOut[++nOut]="<blockquote>"  # подвал страницы
  aOut[++nOut]="<center>" mkGoTo(0) "</center>"  # заголовке
  aOut[++nOut]="<ul>"
  aOut[++nOut]="<table border=0 cellspacing=0 cellpadding=2><tr>"  # навигация в
  aOut[++nOut]="<td><b>Попутные сведения</b> (к "rTyp"-"ancD")"
  aOut[++nOut]="<td></tr></table>"
  aOut[++nOut]="<li>Последний <u>полный</u>"
  aOut[++nOut]="\"<a href=/rating/hits/index.shtml><i><b>Рейтинг"
  aOut[++nOut]="по количеству посетителей</b></i></a>\": за "hDat
  aOut[++nOut]="(от "dat_str(donH)")"
  aOut[++nOut]="<br>сравнивался с <i>предпоследним</i>: за "pDat
  aOut[++nOut]="(от "dat_str(preH)")."
  aOut[++nOut]="<li><i>Прочие</i> данные - по состоянию на "dat_str(hold)"."
#   if(txtM){
#     aOut[++nOut]="<li>Разделы с <i>несколькими</i> (‡) лидерами: <b>"txtM"</b>."
#   }
  aOut[++nOut]="</ul>"
  aOut[++nOut]="<table align=center border=0 cellspacing=0 cellpadding=2>"  # навигация в
  aOut[++nOut]="<tr><td>(copyleft "rYea"</td>"
  aOut[++nOut]="<td bgcolor="color[6]"><b>/\\/.\\/.\\/.</b></td><td>)</td></tr></table>"
}

function mkGoTo(lCur) {                           # сборка строки навигации для титула таблиц
  lBeg=1                                          # начало очередного интервала
  lEnd=lBeg+tGap-1                                # и его конец
  if ((lEnd-lMax)>0) lEnd=lMax                    # но не выше общего конца
  sNav="<center>Интервал <b>N</b>: <b>"           # начало строки навигации
  for(j0=1;j0<=lMax;j0++) {
    if (j0==lBeg) {                               # компоновка очередного элемента навигации
      if ((lBeg-1)>0) sNav=sNav " | "             # первый разделитель опускаем
      if (lBeg==lCur) sNav=sNav "<big>" lBeg "-" lEnd "</big>"  # это у своей таблицы, v-иначе
      else {
        sNav=sNav "<a title=\"Перейти к интервалу: " lBeg "-" lEnd "\" href=\"#"
        sNav=sNav ancD ancW j0"\">" lBeg "-" lEnd "</a>" # оформление как ссылка на ЯКОРЬ
      }
      lBeg=lEnd+1                                 # сдвинем границы на шаг tGap
      lEnd=lEnd+tGap
      if ((lEnd-lMax)>0) lEnd=lMax
    }
  }
  sNav=sNav"</b></center>"                        # завершение строки
  return(sNav)
}

function mkCap0() {                             # сборка доп.над.шапки (только для первой таблицы)
  tmp0=     "<tr align=center bgcolor="color[0]">"
  tmp0=tmp0 "<td colspan=5>Самые посещаемые <b>разделы</b></td>"
  tmp0=tmp0 "<td bgcolor="color[1]" colspan=5>Самые посещаемые <b>произведения</b> /в этих разделах/</td>"
  return( tmp0 "</tr>" )                          # шапка готова
}

function mkCap() {                                # сборка шапки таблиц
  tmp0= "<tr align=center bgcolor="color[flag]">" # шапки начало; v-место:хиты
  tmp0=tmp0 "<td bgcolor="color[6]" title=\"Местов списке, Хиты за сутки\"><b>N</b>: Успех</td>"
  tmp0=tmp0 "<td><a title=\"Раздел за 12мес: место, хиты\"><b>Автор</b></a></td>" # Автор
  tmp0=tmp0 "<td bgcolor="color[6]"><b>Девиз</b></a></td>"    # Произведение
  tmp0=tmp0 "<td><small><b>Объём</b>/шт.</small></td>"
  tmp0=tmp0 "<td bgcolor="color[6]"><small><b>Обновлялось</b></small></td>"
  tmp0=tmp0 "<td title=\"Хиты за сутки\"><small><b>Успех</b></small></td>"
  tmp0=tmp0 "<td bgcolor="color[6]" title=\"Самый популярный в разделе\"><small><b>Лидер</b></small></td>"
  tmp0=tmp0 "<td><a title=\"Дата последнего изменения текста\"" # Создан +v
  tmp0=tmp0 "><small><b>Изменено</b></small></a></td>" # ^+ Создан
  tmp0=tmp0 "<td bgcolor="color[6]"><a title=\"Текст в килобайтах\""  # Объём +v
  tmp0=tmp0 "><small><b>Объём</b></small></a></td>" # ^+ Объём
  tmp0=tmp0 "<td><a title=\"Текста\"><small><b>Форма</b>: Жанр (жанры)</small></a></td>" # Форма: Жанр(ы)
  return( tmp0 "</tr>" )                          # шапка готова
}

function mkTabLn(nLn,tLn) {     # выдача строки номер nLn (строка исходного списка за номером tLn)
  flag=!flag                                      # чередующаяся раскраска строк
  aOut[++nOut]="<tr "substr(nLn+1000,2)" align=center bgcolor="color[flag]">" # собираем строку...
  aOut[++nOut]="<td "a01[tLn]" title=\""a00[tLn]"\"><b>"nLn"</b>: "a14[tLn]"</td>" # Место(N): хиты
  if(a10[tLn] in a90)ownColor=" bgcolor="color[12]  #  "скрытый" автор /потеряшка/ 2023.07.08
  else               ownColor=""
  aOut[++nOut]="<td align=left"ownColor" title=\"место:"a12[tLn]", хиты:"a13[tLn]"\"><a href=/"a10[tLn]"/ id=\""ancA[tLn]"\">"a11[tLn]"</a></td>"
  aOut[++nOut]="<td"a03[tLn]"><i><font color=Maroon>"a15[tLn]"</font></i></td>"  # Девиз 
  aOut[++nOut]="<td"a02[tLn]" "a06[tLn]">"a16[tLn]"</td>"  # Объём
  aOut[++nOut]="<td "a18[tLn]">"rusDat(a17[tLn])"</td>"  # Обновлялось v-Успех
  aOut[++nOut]="<td><a href=/"a10[tLn]"/stat.shtml#"a20[tLn]".shtml id=\""a20[tLn]".shtml\">"a22[tLn]"</a></td>"
  if(a10[tLn]==nUrl)nvvColor=" title=\"это то, на что вы смотрите ;)\" bgcolor="color[10]
  else              nvvColor=""                   #  ^-моё? МОЁ! Моё... ;)
  aOut[++nOut]="<td"nvvColor" align=left><a href=/"a10[tLn]"/"a20[tLn]".shtml id=\""ancA[tLn]":"a20[tLn]"\">"a21[tLn]"</a></td>"
  aOut[++nOut]="<td"a34[tLn]">"rusDat(a31[tLn])"</td>"  # Изменено ^-Лидер
  aOut[++nOut]="<td"a04[tLn]" align=right "a05[tLn]">"a32[tLn]"k</td>"  # Объём
  aOut[++nOut]="<td>"a40[tLn]"</td>"  # Форма: Жанр (жанры)
  aOut[++nOut]="</tr>"
  return                                          # строку собрали, вываливаем!
}

# $1  $2   $3   $4   $5   $6   $7   $8   $9       $10        $11    $12  $13    $14    $15   $16    $17   $18
# Luck new  hitP numP sizK sizN hitT txtN sizT     datP       endD   id_P "namP" "moto" id_T "namT" "jnrT" fNam
# 5527 0  218357 13   936  6    4937  1    7   2020.11.15 2020.08.31 z/zhan "Tony" "kjujdj•xbnfntkz" flibustain "Известные•мне•зеркала•Флибусты•и•Либрусека" "<a•href=/type/index_type_5-1.shtml>Статья</a>:" z~zhan

{         # загрузка исходного списка (с разборкой и оформлением значений, отображаемых в ячейках)
  if(match($1,/^[0-9]+$/)){                       # признак - "строка основных данных"
    if($7-minR>=0){                          # !!! успех раздела МОЖЕТ быть и ниже minR
      jj++
      if($12 in a0a){                # раздел БЫЛ в старом списке
        j0=a0a[$12]-jj
        if(j0>0){                        # auth, раздел съехал вверх
          auUp++                                  # auth, счётчик, раздел, сдвиг вверх
          a00[jj]="Сдвиг:"j0"-^"                  # auth, сдвиг ()
          a01[jj]=" bgcolor="color[4]             # auth, цвет фона ()
        }else if(j0<0){                  # auth, раздел съехал вниз
          auDn++                                  # auth, счётчик, раздел, сдвиг вниз
          a00[jj]="Сдвиг:"0-j0"-v"                 # auth, сдвиг ()
          a01[jj]=" bgcolor="color[3]             # auth, цвет фона ()
        }else{                                    # auth, "на месте"
          a00[jj]="Сдвиг:0=="                     # auth, сдвиг ()
          a01[jj]=""                              # auth, цвет фона ()
        }
      }else{                              #  раздел в старом списке ОТСУТСТВУЕТ, оформляем "новьё"
        auNw++                                    # auth, счётчик, "новый"
        a00[jj]="Новый"                           # auth, сдвиг (новый)
        a01[jj]=" bgcolor="color[5]               # auth, цвет фона (новый)
      }
      a14[jj]=sprintf("%2.1f",$1/10)              # автор, "Успех" за цикл обновления статистик
      a10[jj]=$12                                 # автор, id_P - урл раздела (мой - n/nosow_w_w;)
      ancA[jj]=substr($12,1,1)"~"substr($12,3)    # формат: "aChr~aNik"
      a11[jj]=unpack($13)                         # автор, namP - ФИО
      a12[jj]=$4                          # автор, numP - позиция раздела в рейтинге посещаемости
      if(!a12[jj])a90[$12]++                  #  СКРЫТЫЙ раздел, учтём произведение из 2023.06.23
      a13[jj]=$3                            # автор, hitP - хиты раздела (в рейтинге посещаемости)
      a15[jj]=unpack($14)                         # "moto"
      if($2)a03[jj]=" title=\"создано намедни\" bgcolor="color[5]  # new Author
      else a03[jj]=""
      a16[jj]="<b>"$5"</b>k/"$6
#        a0k[aj[12]]=$5                              # объём раздела в кбайтах
#        a0n[aj[12]]=$6                              # наполнение в штуках ;)
      if($12 in a0a){                # раздел в старом списке БЫЛ, оформляем "разницу"
        if((a0k[$12]-$5<0)||(a0n[$12]-$6<0)){
          upAu++
          a02[jj]=" bgcolor="color[4]
          a06[jj]=" title=\"Наполнение стало больше\""
        }else if((a0k[$12]-$5<0)||(a0n[$12]-$6<0)){
          dnAu++
          a02[jj]=" bgcolor="color[3]
          a06[jj]=" title=\"Наполнение стало меньше\""
        }
      }else{
        a02[jj]=" bgcolor="color[5]           # auth, цвет фона (новый)
        a06[jj]=" title=\"Новый раздел\""
      }
      a17[jj]=$10                                 # раздел, datP - дата "Обновлялось"
# (разделы) выявим/отметим "свежак" и "старьё"...
      if(dBeg<=a17[jj]){                          # "свежак" (с начала нашего интервала)
        a18[jj]=" title=\"обновлено намедни\" bgcolor="color[4]
        curP++
      }else if(oldD>a17[jj]){                     # "старьё" (год и более назад)
        a18[jj]=" title=\"обновлено давно\" bgcolor="color[11]
        oldP++                                    # счётчик "давно"
      }else  a18[jj]=""                           # норма...
      a22[jj]=sprintf("%2.1f",$7/10)            # текст, hitT - хиты текста (в статистике раздела)
#       if($8-1>0){                                 #  число текстов-лидеров txtN в разделе >одного
#         txtM++
#         a22[jj]=a22[jj]"‡"
#       }
      a20[jj]=$15                               # текст, id_T - имя файла (без расширения .shtml)
      a21[jj]=unpack($16)                         # текст, namT - заголовок
      a31[jj]=$11                                 # текст, endD - дата последнего изменения
# (лидеры) выявим/отметим "свежак" и "старьё"...
      if(dBeg<=a31[jj]){                          # "свежак" (с начала нашего интервала)
        a34[jj]=" title=\"изменено намедни\" bgcolor="color[4]
        curT++
      }else if(oldD>a31[jj]){                     # "старьё" (год и более назад)
        a34[jj]=" title=\"изменено давно\" bgcolor="color[11]
        oldT++                                    # счётчик "давно"
      }else  a34[jj]=""
# $1  $2   $3   $4   $5   $6   $7   $8   $9       $10        $11    $12  $13    $14    $15   $16    $17   $18
# Luck new  hitP numP sizK sizN hitT txtN sizT     datP       endD   id_P "namP" "moto" id_T "namT" "jnrT" fNam
# 5527 0  218357 13   936  6    4937  1    7   2020.11.15 2020.08.31 z/zhan "Tony" "kjujdj•xbnfntkz" flibustain "Известные•мне•зеркала•Флибусты•и•Либрусека" "<a•href=/type/index_type_5-1.shtml>Статья</a>:" z~zhan
      split($9,aq,".")
      a32[jj]=aq[1]                               # текст, sizT - объём в килобайтах
      a33[jj]=aq[2]                     # текст, sizB - "1"==объёмOk, "0"=="доступ ... ограничен"
      if($12 in a0a){                # раздел в старом списке БЫЛ, оформляем "разницу"
        if(a0t[$12]==$15){                           #  id_T (текста-лидера) был в старом
          j0=aq[1]-a0z[$12]
          if(j0>0){
            upTx++                                # объём+ текста
            a04[jj]=" bgcolor="color[4]
            a05[jj]=" title=\"+"j0"k\""
          }else if(j0<0){
            dnTx++                                # объём- текста
            a04[jj]=" bgcolor="color[3]           #  3
            a05[jj]=" title=\""j0"k\""
          }
        }else{                                    # это новый текст того же автора
          nwTx++                                  # новый текст
          a04[jj]=" bgcolor="color[5]
          a05[jj]=" title=\"New\""
        }
#        a0t[aj[12]]=aj[15]                        # id_T (текста-лидера)
#        a0z[aj[12]]=aj[9]                         # объём текста
      }
      a40[jj]=unquot(unpack($17))                 # текст, jnrT - жанр(ы) (может отсутствовать)
# Жанр(ы)
    split(a40[jj],aq,":")                         # разделим форму и жано(ы)
    if(aq[2]=="") aq[2]="--"                      # два дефиса подряд смотрятся красивше (jnrT;)
    a40[jj]="<b>"aq[1]"</b>: "aq[2]
    } # else print(sprintf("%4s%4s",NR,$7),$12,$13,$15,$16)>"z.#"
  }else{
# /^;1aPre .+ hDat:20..\.(.+)\.(.+) hGap:([0-9]+)\..+ hPag:([0-9]+) .+ done:(20[\.0-9]+) /,aj)){
#     pDat=aj[2]"/"aj[1] # за дата
#     preH=aj[5]         # от дата
#     pPag=aj[4]                                    # страниц aHits
# /^;1aCur .+ hDat:20..\.(.+)\.(.+) hGap:([0-9]+)\..+ hPag:([0-9]+) .+ done:(20[\.0-9]+) /,aj)){
#     hDat=aj[2]"/"aj[1]
#     donH=aj[5]
#     hPag=aj[4]                                    # страниц aHits
#     hMax=aj[3]                                    # хиты раздела-лидера aHits
# /^;5H minL:([0-9]+) .+ txtM:([0-9]+) done:([\.0-9]+) /,aj)){
#     minL=sprintf("%2.1f",aj[1]/10)                # нижняя граница "успеха" в выборке
# /^;5G .+ done:([\.0-9]+)\//,aj)){
#     hold=aj[1]                                    # дата сборки "yyyy.MM.dd"

# ;1aPre hits 2:xNam 446:xNum 101267:xPas 2021.04.22:hDat 1018:hPag 801964:hMax 1009:pGet 2021.04.24:done gap1:14:43-15:04 
# ;1aCur hits 2:xNam 445:xNum 101285:xPas 2021.04.22:hDat 1018:hPag 806772:hMax 1018:DnLd 2021.04.27:done 25:mins gapT~14:53-15:18~ 
# ;5G gapP 3:gapD 100:minL 758:gNum 2021.04.27:done 19:02
# ;5H gapT 100:minL 0:nulS 0:badS 0:nulP 0:badP 0:tNul 2:tBad 0:unAv 2:txtM 133:dnLd 2021.04.27:done 6:mins 20:10-20:16:gapT 
    if(match($0,/^;1aPre .+ 20..\.(.+)\.(.+):hDat /,aj))pDat=aj[2]"/"aj[1] #  pre дата aHits по СИ
    if(match($0,/^;1aPre .+ ([\.0-9]+):done /,aj))      preH=aj[1] #  дата "скачан" (aHits)
    if(match($0,/^;1aPre .+ ([0-9]+):hPag /,aj))        pPag=aj[1] #  страниц aHits
    if(match($0,/^;1aCur .+ 20..\.(.+)\.(.+):hDat /,aj))hDat=aj[2]"/"aj[1] # дата aHits по СИ
    if(match($0,/^;1aCur .+ ([\.0-9]+):done /,aj))      donH=aj[1] #  дата "скачан" (aHits)
    if(match($0,/^;1aCur .+ ([0-9]+):hPag /,aj))        hPag=aj[1] #  страниц aHits
    if(match($0,/^;1aCur .+ ([0-9]+):hMax /,aj))        hMax=aj[1] #  хиты раздела-лидера aHits
    if(match($0,/^;5G .+ ([0-9]+):minL /  ,aj))         minL=sprintf("%2.1f",aj[1]/10) #  minLuckP
    if(match($0,/^;5H .+ ([\.0-9]+):done /,aj))         hold=aj[1] #  дата "расчёт"
    ancD=substr(hold,3,2) substr(hold,6,2) substr(hold,9,2) # дата сборки "yyMMdd"
  }
}

function dat_str(d0,d2) {       # "2020.05.23" -> "23 мая" (d2==nul) or "23 мая 2020 г." (d2!=nul)
  d1=split(d0,ad,".")
  if(!d2)return("<b>"ad[3]+0" "nMth[ad[2]+0]"</b>")
  else   return("<b>"ad[3]+0" "nMth[ad[2]+0]"</b> "ad[1]" г.")
}

function rusDat(s0) {                             # 2019.19.25 -> 25.19.2019
  split(s0,aq,".")
  return(aq[3]"."aq[2]"."aq[1])
}

function unpack(j1) {               # восстановление литерала, свёрнутого при сборе сведений с СИ
  if (substr(j1,1,1)=="\"") j1=substr(j1,2,length(j1)-2)  # откусим обрамляющие двойные кавычки
  j2=""                                           # "•" -> пробел
  while(n=index(j1,"•")) { j2 = j2 substr( j1, 1, n-1 ) " "; j1 = substr( j1, n+1 ) }
  j1=j2 j1
  j2=""                                           # "”" -> двойные кавычки
  while(n=index(j1,"”")) { j2 = j2 substr( j1, 1, n-1 ) "&quot;"; j1 = substr( j1, n+1 ) }
  j1=j2 j1
  return( j1 )
}

function unquot(j1) {       # восстановление литерала "жанр(ы), свёрнутого при сборе сведений с СИ
  j2=""                                           # '&quot;' -> '"'
  while(n=index(j1,"&quot;")) { j2 = j2 substr( j1, 1, n-1 ) "\""; j1 = substr( j1, n+6 ) }
  j1=j2 j1
  return( j1 )
}

END{                            # данные загружены и разобраны, оформляем рейтинг и приложения...
  if(!(1 in ARGV)){
    print "NO input file!"                        # защита от случайного пуска
    exit -1
  }
  if(hold=="") {
    print "NO date to bind siTopAuthors"
    exit -1
  }
#   if(pDat==-1)aVar["pDat"]=1                      #  pre дата aHits по СИ
#   if(preH==-1)aVar["preH"]=1                      #  pre дата "скачан" (aHits)
#   if(pPag==-1)aVar["pPag"]=1                      #  pre страниц aHits
#   if(hDat==-1)aVar["hDat"]=1                      #  дата aHits по СИ
#   if(donH==-1)aVar["donH"]=1                      #  дата "скачан" (aHits)
#   if(hPag==-1)aVar["hPag"]=1                      #  страниц aHits
#   if(hMax==-1)aVar["hMax"]=1                      #  хиты раздела-лидера aHits
#   if(minL==-1)aVar["minL"]=1                      #  минимальная "удача" раздела
#   if(hold==-1)aVar["hold"]=1                      #  дата "расчёт"
#   if(j0=length(aVar)){
#     tmp0="? no var(s):"
#     for(j1 in aVar)tmp0=tmp0" "j1
#     print(tmp0" in "FILENAME)
#     exit -1
#   }
  tMax=jj
  if(500-tMax>=0)tGap=50
  if(250-tMax>=0)tGap=25
  mkPage()

  print("<blockquote>")                   >fOut
  print(info)                             >fOut   #  служебная отметка (для порядку)
  if(nHnY)for(jj=1;jj<=nHnY;jj++)print(aHnY[jj])  >fOut
  for(jj=1;jj<=nHdr;jj++)print(aHdr[jj])  >fOut
  for(jj=1;jj<=nHd1;jj++)print(aHd1[jj])  >fOut
  for(jj=1;jj<=nTb1;jj++)print(aTb1[jj])  >fOut
  for(jj=1;jj<=nTbl;jj++)print(aTbl[jj])  >fOut
  print(Ttl1)                             >fOut
  for(jj=1;jj<=nOut;jj++)print(aOut[jj])  >fOut
  print(info)                             >fOut   #  служебная отметка (для порядку)
  printf("%s","</blockquote>")            >fOut

  print("<blockquote>")                   >fPre
  print(inf2)                             >fPre
  if(nHnY)for(jj=1;jj<=nHnY;jj++)print(aHnY[jj])  >fPre
  for(jj=1;jj<=nHdr;jj++)print(aHdr[jj])  >fPre
  for(jj=1;jj<=nHd2;jj++)print(aHd2[jj])  >fPre
  for(jj=1;jj<=nTb2;jj++)print(aTb2[jj])  >fPre
  for(jj=1;jj<=nTbl;jj++)print(aTbl[jj])  >fPre
  print(Ttl2)                             >fPre
  for(jj=1;jj<=nOut;jj++)print(aOut[jj])  >fPre
  print(inf2)                             >fPre
  printf("%s","</blockquote>")            >fPre

  endS=mktime(substr(hold,1,4)" "substr(hold,6,2)" "substr(hold,9,2)" 00 00 00")  # seconds "до"
  print "--- " strftime("%H:%M:%S") " Ends..."  # закончили работу (оживляж;)
# 2020.11.29, nvv, creation by siTop-fHtml.awk (2020.11.18)
# 2020.12.03, nvv, it works! (off line, alas)
# 2021.01.11, nvv, it works ok... ;) пока без опорного предыдущего списка
# 2021.01.29, nvv, всё запланированное обустроено, включая 2 и более лидеров в разделе ("‡")
# 2021.02.06, nvv, отключена отладочная выдача в "z.#" отвергнуты записей с низкими хитами
#   + уточнен сбор данных по призёрам предыдущего приложения (именно его призёры,
#     но не присутствовавшие лишь в прошлом ОБЩЕМ списке)
# 2021.03.11, nvv, мелкая неточность вычленения даты предыдущего списка + косметика
# 2021.03.21, nvv, new text color+title added
# 2021.04.24, nvv, new text color+title off
# 2021.06.10, nvv, мелкая косметика
# 2021.07.09, nvv, исправлена неточность в маркировке цветом текстов-лидеров
# 2022.01.13, nvv, исправлена неточность в выявлении новогоднего периода
# 2022.06.07, nvv, мелочёвка
# 2022.10.19, nvv, в шапку добавлена ссылка на текст: "Регистрация в СИ""
# 2022.12.01, nvv, текст: "Регистрация в СИ" перестал быть New
# 2023.07.08, nvv, добавоена поддержка скрытых разделов место "0" /numP/ в рейтинге по хитам
# + хоть это и приложение к siTopTexts, но таки тоже рейтинг, что теперь явно сказано в заголовке
}

    Имя файла скрипта: "iNetR.cmd" V-текст скрипта-V
- - - - - - - - -
@if not defined nJob @echo off
echo %date% %time:~0,8% %~nx0 Begs
:: iNetR.cmd - оформление в HTML списка топовых разделов прошедшего цикла обновления статистик
:: + 20??.??.??.*.gAuth (like 2021.05.19.6.gAuth) - оформляемый последний список
:: + 20??.??.??.*.gAuth (like 2021.05.14.3.gAuth) - оформляемый ПРЕД последний список

:: проверить-найти:
:: - свежий *.gAuth, например: 2021.04.27.4.gAuth
:: - старый *.gAuth, например: 2021.04.24.3.gAuth
:: - свежий *.gAuth.html, например: 2021.04.27.4.gAuth.html
:: - старый *.gAuth.html, например: 2021.04.24.3.z.gAuth.html


:: 2021.05.29, nvv, creation на основе fHtml.cmd от 2021.05.25
:: 2021.06.13, nvv, confirmation to move PREV data off
set nJob=iNetR
if NOT defined jDat set jDat=%date%
set ok2upDt=%1
set aHtm=%~dp0%nJob%.awk
::  wDir - общее рабочее место
set wDir=%~d0\Jobs
::  sDir - общее складское хранилище
set sDir=%~d0\Stor
::  dDir - склад для уже ненужных списков 20??.??.??.gAuth
set dDir=%sDir%\Text\zOld.gAuths
if NOT exist %dDir% set errInf=NO %dDir% found & goto nExit
::  hDir - склад под выходные файлы с таблицами *.html
set hDir=%sDir%\Html
set pDir=%hDir%\gAuths
if NOT exist %hDir%\ mkDir %hDir%
if errorLevel 1 set errInf=fail mkDir %hDir% & goto nExit

::  fOld - прошлый опорный список для "siTopTexts"
set fOld=
::  fCur - свежий список для "siTopTexts" - 1k популярных текстов (со всеми атрибутами)
set fCur=
for %%A in (%wDir%\20??.??.??.*.gAuth) do call :findCurOld %%A
if NOT defined fOld set errInf=!? NO old 20*.gAuth found & goto nExit
if defined errInf goto nExit

set fHtm=%hDir%\%dCur%.%gCur%.gAuth.html
echo prev:%fOld%
echo curr:%fCur%
echo fHtm:%fHtm%
if NOT exist %fHtm% goto mkHtm

:: итоговый файл есть. если нужно прибрать - идём прибирать, иначе выясняем...
if defined ok2upDt goto upDt

:: запрос (конечный HTML уже есть!)
set tmp0=
echo fHtm found; reBuild it? [yes] == y
set /p tmp0=?[no]?
if /i NOT %tmp0%. == y. (
  echo job canceled {NO to reBuild}
  exit
)
:mkHtm собственно оформлялка ("от и до";)
gawk.exe -f %aHtm% %fCur%
if errorLevel 1 set errInf=!!! X someThing's wrong with %~n0.awk & goto nExit
:: блокируется оптичивание преобразованного в HTML
goto nExit

:upDt  запрос (точно ненада?)
set tmp0=
echo confirm PREV data OFF? [yes] == y
set /p tmp0=?[no]?
if /i NOT %tmp0%. == y. (
  echo job canceled {NO to reBuild}
  exit
)
:: уберём уже ненужный старый список рейтинга на склад Text\zOld.gAuths
if exist %fOld% (
echo move %fOld% to %dDir%
move /Y %fOld% %dDir%
if errorLevel 1 set errInf=!! fail to move old *.gAuth to %dDir% & goto nExit
)
:: опубликованный %dCur%.gAuth.html (убрать)
set tmp0=%hDir%\%dCur%.%gCur%.gAuth.html
if exist %tmp0% (
echo move %tmp0% to %pDir%
move /Y %tmp0% %pDir%
if errorLevel 1 set errInf=!! fail to move %tmp0% to %pDir% & goto nExit
)
:: опубликованный %dOld%.%gOld%.gAuth.z.html ("предыдущее" удалить)
set tmp0=%hDir%\%dOld%.%gOld%.gAuth.z.html
if exist %tmp0% (
echo erase %tmp0%
erase %tmp0%
if errorLevel 1 set errInf=!! fail to erase %tmp0% & goto nExit
)

:nExit
if defined errInf (
  echo ! %errInf%
  set errInf=
)
echo %date% %time:~0,8% Job:%nJob% Ends
goto :eof

:findCurOld ::  dOld dCur - даты в именах файлов опорного и текущего списков
if defined fOld if defined fCur set errInf=too many *.gAuth files in %wDir% & goto :eof
set fOld=%fCur%
set gOld=%gCur%
set dOld=%dCur%
set fCur=%1
set dCur=%~n1
set gCur=%dCur:~11%
set dCur=%dCur:~0,10%
goto :eof

    Имя файла скрипта: "zSchk.awk" V-текст скрипта-V
- - - - - - - - -
BEGIN{                                            # zStatChk.awk scan *.stat.htm только по датам
# 2022.05.14, nvv, заметно переделано: расширена и реаранжирована выдача в файд и на экран
#   + исправлены мелкие неточности + убран лишний оживляж
  if(ARGV[1]=="")exit                             # защита от случайного пуска
# массив имён месяцев для преобразования их в числа с лидирующим нулём 01..12
  mNum["Jan"]="01"                                # .1 January . Jan. 31 . winter wint
  mNum["Feb"]="02"                                # .2 February .Feb. 28/29
  mNum["Mar"]="03"                                # .3 March . . Mar. 31 . spring sprn
  mNum["Apr"]="04"                                # .4 April . . Apr. 30
  mNum["May"]="05"                                # .5 May . . . May. 31
  mNum["Jun"]="06"                                # .6 June . . .Jun. 30 . summer summ
  mNum["Jul"]="07"                                # .7 July . . .Jul. 31
  mNum["Aug"]="08"                                # .8 August . .Aug. 31
  mNum["Sep"]="09"                                # .9 September Sep. 30 . autumn autm
  mNum["Oct"]="10"                                # 10 October . Oct. 31
  mNum["Nov"]="11"                                # 11 November .Nov. 30
  mNum["Dec"]="12"                                # 12 December .Dec. 31 . winter wint
# объявим массивыы для общего использования в подпрограммах
  split("",aDatN)                                 # счётчики пар дат: "datS"-"dat01"
  split("",aNumD)                                 # счётчики строк с записями в файлах выдачи
  split("",aTot)
  split("",aBad)
  split("",aSel)      # в ячейках статистики: url,title,hits:all,12mon,mon0..monC-11,day0..day0-61
  mark=""                                         # max dateTime statRefresh
  nTot=0
  nEnd=0
  nBad=0
  sNul=0
  sTry=0
  for(str2 in ENVIRON){                       # ! регистр букв в имени переменной может быть любой
    str1=tolower(str2)                            # Temp и temp - РАЗНЫЕ имена для gAwk...
    if(str1=="fhit")fHit=ENVIRON[str2]            # опорный aHits список
    if(str1=="snul")sNul=ENVIRON[str2]          #  нулевые статистики (из CMD - здесь их не видно)
    if(str1=="stry")sTry=ENVIRON[str2]            #  номер прохода за сегодня
    if(str1=="sawk")sAwk=ENVIRON[str2]            #  путь к скрипту + он сам
    if(str1=="ratw")ratW=ENVIRON[str2]            #  средняя скорость скачивания "секунд на файл"
  }
  if(sAwk=="")sAwk="U:\\zSchk\\zSchk.awk"
  j1=split(sAwk,aj,"\\")
  if(j1==1)fDir=".\\"
  else{
    fDir=aj[1]"\\"
    for(j0=2;j0<j1;j0++)fDir=fDir aj[j0]"\\"
  }
  if(fHit==""){                                   # а списка нет, выясним влоб по логу "_siTop.#"
    id_P="U:\\Jobs\\_siTop.#"
    while((getline line<id_P)>0){       # ищем последнюю запись успешного скачивания этого списка
# ;1A hits 4:xNam 459:xNum 102832:xPas 2022.04.21:hDat 529943:hMax 1033:hPag 1034:dnLd 0.08~ratW 2022.04.23:done 06:08:59~wrks 15:00 
      if(match(line,/^;1A hits .+ 20([\.0-9]{8}):hDat /,aj))fHit=aj[1]
    }
    close(id_P)
    fHit="U:\\Jobs\\20"fHit".aHits"               # строим полное имя нужного списка
  }
#  201661   21  846    3 k/kadawr "Кадавр" "no•comments"
# ]  hitP numP sizK sizN id_P     "namP"   "moto"
#       1    2    3    4 5         6        7
#   print(". load "fHit" & *.stat.htn")             #  заполним массив атрибутов разделов в *.aHits
  nHit=0
  while((getline line<fHit)>0){                   # fHit: like 2020.03.01.aHits
    split(line,aj)                                # aj[5] == k/kadawr
    if(match(aj[1],/^[0-9]+$/)){
      aHitP[aj[5]]=aj[1]                          # aj[1] == 201661
      aNumP[aj[5]]=aj[2]                          # aj[2] == 21
      aNamP[aj[5]]=aj[6]                          # aj[6] == "Кадавр"
      nHit++
    }
  }
  close(fHit)                                     # всё/не_всё, - старый список тогось
  if(nHit==0)exit
}

{                                                 # // -=- основной блок скрипта begs
  if(FNR==1){           # секция завершения работ с одним файлом и подготовки к работе со следущим
    if(NR!=1)chkSteps()                           # чем закончилась обработка предыдущего файла?
    fPth=FILENAME
    fNam=split(FILENAME,aj,"\\")                  # NEW/NEXT входной файл as-is (с полным путём)
    fNam=split(aj[fNam],aj,".")                   # файл: чистое nam.ext (like "0-0.stat.htm")
    fNam=aj[1]                                    # fNam чистое имя (like "0-0" in "0-0.stat.htm")
    step=0        # 0-владелец, 1-дата обновления, 2-id раздела, 3-разбор строк таблиц статистики
    id_P=""
    inTab=0                                       # сначала - мы НЕ "в таблице"
    numT=0                                        # счётчик таблиц в файле статистики
    numR=0                                        # счётчик строк в текущей таблице
    selN=0
    int8++
#     printf("%s",".")                              # оживляж...
    if(int8==50){
      int9++
      int8=0
#       print(sprintf("%3s",int9))
    }
    nTot++
  }
  if(step==0) {                                   # ищем/проверяем ФИО автора
# <h3>Статистика раздела &quot;<a href=./>Влад</a>&quot;:</h3>
    if (match($0,/<h3>Статистика раздела \&quot;<a href=\.\/>(.*)<\/a>\&quot;:<\/h3>/,aj)) {
      if(aj[1]==""){
        nextfile                                  # что-то не так с датой обновления
      }
      step++                                      # проверка ОК
    }
  }else if(step==1){                              # вычленеие даты обновления статистики
# Статистика рассчитывается раз в сутки. (Thu Apr 28 00:56:14 2022)
    if(match($0,\
/<li><i>Статистика рассчитывается .+\. \(.+ (...) +([0-9]+) (..):(..):(..) (20[0-9][0-9])\)<\/i>/,aj)){
      if(length(aj[2])==1) aj[2]="0"aj[2]         # причешем первые дни месяца "1" -> "01"
      ticS=mktime(aj[6]" "mNum[aj[1]]" "aj[2]" 12 00 00") # тики на 12 часов даты статистики
      datS=aj[6]"."mNum[aj[1]]"."aj[2]
      timS=aj[3]":"aj[4]":"aj[5]
      if(mark<datS"~"timS){
        mark=datS"~"timS
        sSec=mktime(aj[6]" "mNum[aj[1]]" "aj[2]" "aj[3]" "aj[4]" "aj[5])
      }
      if(strftime("%Y.%m.%d",ticS)!=datS){
        nextfile                                  # что-то не так с датой обновления
      }
      step++                                      # дату статистики оптичили
    }
  }else if(step==2){                              # ищем authID (!должен быть)
#[<a href=http://top.mail.ru/pages?id=77427&period=0&date=2018-10-02&filter_type=0
#&filter=n/nosow_w_w&pp=20&gender=0&agegroup=0>по дням</a>]
    if (match($0,/\[<a href=http.+&filter=([0-9a-z]\/[\-0-9a-z_]+)&pp=.+>по дням<\/a>]/,aj)) {
      id_P=aj[1]
      step++                                      # проверка ОК
    }
  }else if(step==3){                # таблицы статистики, просмотр строк, сборка данных в ячейках
    if($0=="<!--------- Подножие ------------------------------->"){  # разбор закончен
      step++
      nextfile
    }
# ищем начало таблицы, вычленяем даты...
    if(int0=index($0\
,"<table border=1 cellspacing=0 cellpadding=0><tr><td><td colspan=2><b>Итого</b></td>")){
      numT++                                      # new table
      numR=1                    # певая строка каждой таблицы - календарь хитов статистики раздела
      buf0=substr($0,int0+80)
      if(numT==1){                                # 1-st table 1-st row -> get col-dates & so-on
        match(buf0,/<tr[^>]*>(.+)<\/tr[^>]*>/,aj) # собственно строка с календарём
        getSels(aj[1])                            # но самую первую такую - разберём-с
        tic0=ticS               # и привяжем дату к нулевому (самому левому) дню посуточных данных
        while(strftime("%d",tic0)!=aSel[16])tic0=tic0-86400 # сдвинем стат.дату к нулевому дню
        dat01=strftime("%Y.%m.%d",tic0-86400)     # полная дата первого дня в интервале 30 дней
        dat30=strftime("%Y.%m.%d",tic0-86400*30)  # полная дата последнего дня в этом же интервале
        dGap=datS"-"dat01
        if(sEnd<datS)sEnd=datS
        if(dEnd<datS)dEnd=dat01
        aDatN[dGap]++                             # счётчики "dat01" дат в статистиках
        aNumD[dGap]++
#      aHitP[aj[5]]=aj[1]                          # aj[1] == 201661
#      aNumP[aj[5]]=aj[2]                          # aj[2] == 21
#      aNamP[aj[5]]=aj[6]                          # aj[6] == "Кадавр"
#        if(aHitP[id_P])aTot[nTot]=dGap sprintf("%6s%8s%9s %s %s",aNumP[id_P],aHitP[id_P],timS,id_P,aNamP[id_P])
#        else           aTot[nTot]=dGap sprintf("%6s%8s%9s %s %s","?"," aHitsNo","?",id_P,"?")
        if(aHitP[id_P])aTot[nTot]=datS"_"timS" "dat01 sprintf("%6s%8s %s %s",aNumP[id_P],aHitP[id_P],id_P,aNamP[id_P])" "fPth
        else           aTot[nTot]=datS"_"timS" "dat01 sprintf("%6s%8s %s %s","?"," aHitsNO","?",id_P,"?")" "fPth
        step=4
        nextfile
      }
    }
  }
}                                                 # // -=- основной блок скрипта ends

function getSels(rowLn){                # разделение строки таблицы статистики на массив значений
  selN=0                                          # sells count
  split("",aSel)
  aSel[0]=""
  while(match(rowLn,/<td[^>]*>/)){                # это объявление ячейки
    selN++                  # 0.1-имя/ссылка; хиты: 2-всего, 3-12мес, 4..15-заМес, 16..77-за сутки
    rowLn=substr(rowLn,RSTART+RLENGTH)            # выкусим объявление ячейки
    f3n2=index(rowLn,"</td")                      # это конец ячейки
    f3s2=substr(rowLn,1,f3n2-1)               # это сама ячейка (с возможными внутренними тегами)
    if(selN==1){                              # в ПЕРВОЙ ячейке - название текста и ссылка на него
      aSel[1]="~"                                 # название текста
      aSel[0]="~"                                 # ссылка на текст: name.shtml
    }else {
      while(match(f3s2,/<\/?[a-zA-Z][1-6a-zA-Z]?[a-zA-Z]*[ \/]?[^>]*>/))  # теги нафик!
          f3s2=substr(f3s2,1,RSTART-1) substr(f3s2,RSTART+RLENGTH)
      aSel[selN]=f3s2                             # очищенное значение ячейки
    }
    rowLn=substr(rowLn,f3n2)                      # выкусим оптиченную ячейку
  }
  if(selN==77)return                              # row HAVE had 77 sells
  nextfile
}

function chkSteps() {                       # проверка чем закончилась обработка очередного файла?
  if(step!=4){                                    # всё штатно?
    nTot--
    nBad++
    if(aHitP[id_P])aBad[nBad]=sprintf("! %6s%8s %s",aNumP[id_P],aHitP[id_P],id_P)
    else           aBad[nBad]=sprintf("! %6s%8s %s", "?",       " aHitsNO", id_P)
  }
}

END{                                              # отстрелялись; ну почти...
  if(ARGV[1]==""){                                # защита от случайного пуска
    print("] step.0 (NO file)")
    exit 1                                        # и на выход с вещами
  }
  if(nHit==0){
    print("] step.0 (no or empty %fHit%: "fHit " )")
    exit 1                                        # и на выход с вещами
  }
#   print(" == " nTot)                              # оживляж заканчивается, последний штрих ;)
  split("",aSel)                                  # финальное оформление итогов обработки
  int2=0                                          # сётчик пар дат
#   totN=0
  for(str0 in aDatN){
    int2++
#     totN=totN+aDatN[str0]
    aSel[int2]=sprintf("%s%7s%6.1f",str0,aDatN[str0],aDatN[str0]*100/nTot)  # datS-dat01 filN %
    split(str0,aj,"-")
    if(aj[2]==dEnd)nEnd=nEnd+aDatN[str0]
  }
  asort(aSel)
  asort(aTot)
  jj=strftime("%Y.%m.%d.%H.%M")
  fOut=fDir jj".Stat.Chk.Lst"
#   if(nTot!=totN){
#     print("!!! bads found: "(nTot-totN))
#     print("!!! bads found: "(nTot-totN))                                    >fOut
#   }
  if(nBad)asort(aBad)
  for(int1=1;int1<=nBad;int1++){
    print(aBad[int1])                                                       >fOut
    print(aBad[int1])
  }
  print("; freshDate - 1-st_day numP/tot hitP freshTime id_P \"namP\"")     >fOut
  print("; freshDate - 1-st_day  numTot    %")
  for(int1=1;int1<=int2;int1++){
    print("; "aSel[int1])
    print("; "aSel[int1]"%")                                                >fOut
  }
  if((nTot==nEnd)&&(sEnd==strftime("%Y.%m.%d")))sMod="v"
  else                                          sMod="!"
# ; 2022.04.27~00:52:44~last 1650981164:sSec fLst:2022.04.28.09.25
# ;0Z sMod:! 2022.04.27:sEnd 2022.04.26:dEnd 31:nEnd 0:nBad 0:sNul 
  print("; "mark"~last  "nEnd":nEnd "nTot-nEnd":miss sMod:"sMod)
  print("; "mark"~last  "nTot-nEnd":miss sMod:"sMod" fLst:"jj)              >fOut
  print("; "strftime("%Y.%m.%d~%H:%M:%S~now.MSK "nBad":nBad "sNul":sNul fLst:"jj,systime()-7*3600),"try:"sTry)
  print("; "strftime("%Y.%m.%d~%H:%M:%S~now.MSK",systime()-7*3600),"try:"sTry,"ratW:"ratW)  >fOut
  print(";sSec:"sSec" "sEnd":sEnd "dEnd":dEnd "nEnd":nEnd "nBad":nBad "sNul":sNul ")  >fOut
#   print(";0Z sMod:"sMod" "sEnd":sEnd "dEnd":dEnd "nEnd":nEnd "nBad":nBad "sNul":sNul ")
  print(";     datS_timS        dat01    numP    hitP id_P \"namP\" fPth")  >fOut
  for(int1=1;int1<=nTot;int1++)print(aTot[int1])                            >fOut
# "      datS-timS        dat01    NumP    HitP id_P \"NamP\" fPth"
# 2021.02.10-01:49:17 2021.02.09     1  888112 m/metelxskij_n_a "Метельский•Николай" U:\zStatChk\#bStatTest\m~metelxskij_n_a.stat.htm
#   int8=split(FILENAME,aj,"\\")
#   sPth=aj[1]
#   for(int9=2;int9<int8;int9++)sPth=sPth "\\" aj[int9]
#   print(mark,"- latest update, totNums: "nTot", done: "jj)
#   print("sPath: "sPth"\\")                                                  >fOut
# 2020.07.18, nvv, creation
# 2020.07.20, nvv, some improves
# 2020.08.15, nvv, some improves
# 2020.09.11, nvv, имя файла *.aHits, если пуста переменная fHist, ищется в общем логе "_siTop.#"
# 2020.11.08, nvv, проблемные статистики теперь собираются в отдельный массив с надлежащим оформлением заприсей
# 2021.06.19, nvv, иной формат выдачи datS+timS (подчерк "_" вместо пробела " ")
}

    Имя файла скрипта: "zSchk.cmd" V-текст скрипта-V
- - - - - - - - -
@if not defined nJob @echo off
rem if NOT %IpOk%. == 2. exit !!!
rem используются только скачанное сегодня. условия завершения работы /+извещение MEW/:
rem - последнее имеет статус "v" и sEnd == jDat
rem - текущее время 10+ часов
rem - предыдущее и последнее совпадают + время после 07:30
rem NB. 07:40 VLD == 00:40 MSK + дополнительно откладываются статистики nvv

:: наличие переменной == останов при блокировке в СИ; закоментировать для отклячения
set ipChk=1

:: 2023.08.13, nvv, смягчена схема аварийных извещений на эгапе запуска /экран без мульти-эдит/
:: ! прочая история - в подвале

:: step.1 основные сеты и прочая подготовка к работе...
set jDat=%date%
::  bDat - минимальное время старта работы со статистиками СИ /для mktime()/
set bDat=%jDat:~0,4% %jDat:~5,2% %jDat:~8,2% 07 23 00
::  maxW - число скачиваемых топовых статистик (в границах тестирования по умолчанию)
set maxW=%1
if NOT defined maxW set maxW=100
rem - настройка + проверка наличия
echo %jDat% %time:~0,8% %~nx0 Begs maxW:%maxW%
::  nJob == zSchk
::  nJob == ySchk
set nJob=%~n0
set errInf=
::  основной и резервный URL Самиздата (желательно работать с резервом)
set siUrl=samlib.ru
if exist U:\siUrl.budclub set siUrl=budclub.ru
::  sNul - кол-во отсутствующих статистик
set sNul=0
::  dnLd - общий счётчик скачек
set dnLd=0
::  wGetSiz - размер скачанного файла (default 1b минимум)
set wGetSiz=1
::  pMin - max пауза между скачиваниями
set pMin=20
::  pSec - сдвиг в секундах для 7-го часового пояса /Москва +7 часов/
set /a pSec=7*3600
::  sTry - счётчик циклов скачивания статистик; первый - всё, прочие - только неподходящее
set sTry=0
::  sAwk - скрипт сканирования статистик и сборки итогового списка
set sAwk=%~pn0.awk
if NOT exist %sAwk% echo NO %sAwk% & exit
::  jDir - место для оперативных файлов
set jDir=%~p0#%nJob%
::  sDir - склад для старых оперативных файлов
set sDir=%~dp0Stor
:: рабочие каталоги; если нет - создаём
for %%A in ( %jDir% %sDir% ) do (
  if NOT exist %%A\*.* mkDir %%A
  if NOT exist %%A\*.* echo fail mkDir %%A & exit
)
::  fHit - входной /ДОЛЖЕН быть/ рейтинг посещаемости разделов СИ
set fHit=
:: найти последний 20??.??.??.aHits и запомнить его имя (например, U:\Jobs\2019.06.27.aHits)
for %%A in (U:\Jobs\20??.??.??.aHits) do set fHit=%%A
if NOT defined fHit echo ? NO 20??.??.??.aHits found & exit

:: А не забыл ли я обиходить прошедщий намедни расчёт?
set jCln=0
for %%A in (U:\Jobs\20??.??.??.dText) do set /a jCln+=1
if %jCln% GEQ 2 (
  echo !!! %jCln% *.dText found
  goto nExit
)
set jCln=0
for %%A in (U:\Jobs\20??.??.??.*.gAuth) do set /a jCln+=1
if %jCln% GEQ 2 (
  echo !!! %jCln% *.gAuth found
  goto nExit
)

:: убираем всякие списки "НЕ сегодня"
for %%A in (%~p0*.Lst) do (
  if %%~nA LSS %jDat% (
    move /y %%A %sDir%
    if errorLevel 1 echo fail move %%A %sDir% & exit
  )
)

rem нет смысла проверять доступ, если работаем с samlib.ru
if %siUrl% == samlib.ru goto skipIpChk
rem батник для проверки своего статуса "спамер"/"забанен" в СИ:
rem ::: http://budclub.ru/cgi-bin/ip_ban_check
rem окультуренный итог выдаётся на экран и в одноимённый лог рядом с батником
rem like: 2022.01.19~20:05 myIp:217.150.72.111 - NO.inSpam - NO.inBan 
rem 2022.01.19, nvv, created
set tmp0=0
set IpOk=0
for /F "tokens=*" %%J in ('wGet -q http://budclub.ru/cgi-bin/ip_ban_check -O-') do call :getLn %%J
if errorLevel 1 echo wGet: budclub.ru ERR %errorLevel% & exit
set tmp0=%time:~0,5%
if %tmp0:~0,1%. == . set tmp0=0%tmp0:~1%
echo %date%~%tmp0% myIp:%myIp% IpOk.%IpOk%
echo %date%~%tmp0% myIp:%myIp% >> %~dp0xIpChk.log
if NOT %IpOk%. == 2. if defined ipChk exit

:: попутно скачаем и свежий лог СИ...
rem call Q:\#-Logs\siLogs.cmd 

:: нет смысла стартовать ДО 07:23 ВЛД /bDat/
for /F "tokens=1-9,*" %%X in (
  ' gawk.exe "BEGIN{t=systime();b=mktime(\"%bDat%\");print(t,b,strftime(\"%%H:%%M:%%S\",b-t,1));exit}"'
) do (
  set jSec=%%X
  set tics=%%Y
  set jTim=%%Z
)
:: если ждать таки нужно...
if %jSec% GEQ %tics% goto skipIpChk
:: ждём, известив о
echo -- waiting %jTim% unTill 07:23
call :waitSecs

:skipIpChk первый проход сходу после старта /jSec, jTim/
call :getAll
if defined errInf goto nExit

:check оцениваем текущую ситуацию /sMod+sSec/ на момент
for /F "tokens=1-9,*" %%X in (
  ' gawk.exe "BEGIN{print(systime(),strftime(\"%%H:%%M:%%S\"));exit}"'
) do (
  set jSec=%%X
  set jTim=%%Y
)
:: "сегодня" + статус "v" == выход
if %sEnd%. == %jDat%. if /i %sMod% == v (
  set info=all OK
  goto finish
)
:: без_изменения после 07:40++ == выход
if %sSec%. == %sPre%. if %jTim% GEQ 07:40 (
  set info=stable
  goto finish
)
:: что угоднр после 10:00 == выход
if %jTim% GEQ 10:00 (
  set info=midDay
  goto finish
)

:: продолжим... ЕСТЬ "сегодня" == ждём 15 минут от %sSec% + 7*3600=25200 /MSK-VLD/, иначе - от %jSec%
if %sEnd%. == %jDat%. (
  set /a tics=%sSec%+%pSec%
) else (
  set tics=%jSec%
)
:: момент завершения паузы
set /a tics=%pMin%*60 + %tics%
if %tics% LEQ %jSec% set /a tics=%jSec%+60
gawk.exe "BEGIN{print(\" -- waiting \"strftime(\"%%M:%%S\",%tics%-systime(),1)\" unTill\",strftime(\"%%H:%%M:%%S\",%tics%));exit}"
:: собственно пауза с %jSec% до %tics% +оживляж ;)
call :waitSecs
rem gawk.exe "BEGIN{while(%tics%-systime()>0){n++;a=systime()+6;if(n%%10){printf(\".\")}else{printf(n/10)};if(!(n%%100))print(\"\");while(a>systime()){}};exit}"
rem echo.*

:: "есть за сегодня" == качаем "НЕ ТО", иначе ВСЁ
if %sEnd%. == %jDat%. (
  call :getMis
) else (
  call :getAll
)
if defined errInf goto nExit
goto check

:finish dnLd - счётчик /формально/ успешно скачанного (НЕ 0b)
rem set itog=%~dp0%jDat%.zSchk.itog.lst
set itog=%~dp0%jDat%.%nJob%.itog.Lst
set tmp0= == %info% at %jTim% tries:%sTry% nBad:%nBad% sNul:%sNul% dnLd:%dnLd%.%nTot%:%gapT% ratW:%ratW%
echo.%tmp0%
echo.;%tmp0% > %itog%
type %jLst% >> %itog%

rem start D:\MEW11\Mew32.exe %itog%

:nExit если была проблем-с - известим всяко
if defined errInf echo !!! %errInf%
:: пауза в конце заказана?
if NOT %2. == . set /p tmp0=Press ^<Enter^> to exit
goto :eof

:getAll чистим накопитель статистик
if exist %jDir%\*.* (
  erase /F /Q %jDir%\*.*
  if errorLevel 1 set errInf=fail erase %jDir%\*.* & goto :eof
)
:: взводим таймер со компанией и +качаем ВСЁ
call :setTimer %maxW%
rem       3 103295       0      0 w/waalon "Ваалон" 
rem ; hitP.1 numP.2 sizK.3 sizN.4 id_P.5 "namP".6 "moto".6 
rem   ' gawk.exe "{if(NR-%maxW%>0)exit;print($5)}" %fHit% ' wrong check 2023.07.15 turned
for /F %%X in (
  ' gawk.exe "{if(n-%maxW%>=0)exit;if(match($1,/^[0-9]+$/)){print($5);n++}}" %fHit% '
) do (
  call :chkGet %%X
  if defined errInf goto :eof
)
:: остановим "таймер" + смотрим, что скачалось...
call :stopTimer
goto :eof

:getMis step.5 взведём таймер со компанией и +переКачиваем неподходячее
call :setTimer %miss%
rem ;   datS_timS.1      dat01.2   numP.3 hitP.4 id_P.5 "namP" fPth
rem 2022.04.27_00:44:02 2022.04.26    37   51437 f/fox "Fox" #bStatTest\f~fox.stat.htm
for /F %%X in (
  ' gawk.exe "{if((substr($1,1,2)==20)&&(substr($1,1,10)!=\"%sEnd%\")&&($2!=\"%dEnd%\"))print($5)}" %jLst% '
) do (
  call :chkGet %%X
  if defined errInf goto :eof
)
:: остановим "таймер" + посмотрим, что скачалось...
call :stopTimer
:: переход к п.3
goto :eof

:setTimer cNum взвод таймера со компанией
set cNum=%1
set nTot=%1
set /a sTry+=1
echo. -%sTry%- try upDate %nTot% files...
set /a jMax=%cNum%/100
if NOT %cNum:~-2% == 00 set jMax=%jMax%+
set jMax=%jMax%      .
set jMax=%jMax:~0,4%
set cNum=0
for /F %%J in ('gawk.exe "BEGIN{print(systime());exit}"') do set ratW=%%J
goto :eof

:chkGet id_W 
set /a cNum+=1
set id_P=%1
set id_F=%id_P:~0,1%~%id_P:~2%
:: имя локального файла из authID (d/dront -> d~dront)
set wFil=%jDir%\%id_F%.stat.htm
if exist %wFil% (
  erase %wFil%
  if errorLevel 1 set errInf=fail erase %wFil% & goto :eof
)
set wUrl=%siUrl%/%id_P%/stat.shtml
call wGetUrl2fil.cmd %wUrl% %wFil%
if defined errInf goto :eof
if NOT %wGetSiz% == 0 goto show
:: если недоступно...
set /a sNul+=1

:show оживляж скачивания статистик
if %cNum:~-1% == 0 (set mark=%cNum:~-2,1%) else (set mark=.)
gawk.exe "BEGIN{printf(\"%mark%\");exit}"
rem if NOT "%cNum:~-2%" == "00" if NOT "%cNum:~-2%" == "50" goto :eof
if NOT "%cNum:~-2%" == "00" goto :eof
:: по 50 отметок на строку
set tmp0=    %cNum%
set tmp0=%tmp0:~-5,3%.%jMax%
gawk.exe "BEGIN{printf(\" %%s\n\",\"%tmp0%\"strftime(\"%%M:%%S\",systime()-%ratW%,-1));exit}"
goto :eof

:stopTimer стоп таймера, обсчёт фактического результата скачивания
for /F "tokens=1-9,*" %%J in (
  ' gawk.exe "BEGIN{t=systime()-%ratW%;printf(\"%%s %%1.2f\",strftime(\"%%Mmin.%%Ssec\",t,-1),t/%nTot%);exit}" '
) do (
  set gapT=%%J
  set ratW=%%K
)
:: возврат каретки для некруглого общего числа статистик
if NOT %nTot:~-2% == 00 echo.
:: извещение о выяснении расклада по наличным статистикам
echo. == dnLd.%nTot%:%gapT% ratW:%ratW%
:: просмотр наличных статистики, сборка очередного списка
gawk.exe -f %sAwk% %jDir%\*.stat.htm
if errorLevel 1 set errInf=fail scaning *.stat.htm & goto :eof
:: ищем этот самый свеже созданный сегодня список
set jLst=
for %%J in (%~p0%jDat%.??.??.Stat.Chk.Lst) do set jLst=%%J
if NOT defined jLst set errInf=no 'jLst' fail make one more *.stat list at %time% & goto :eof
rem ; 2022.04.29~00:51:52~last sMod:! fLst:2022.04.29.09.11
rem ;sSec:1651153912 2022.04.29:sEnd 2022.04.26:dEnd 94:nEnd 0:nBad 0:sNul 
:: считаем результаты обсчёта из шапки свежего списка
set sMod=
for /F "tokens=1-9,*" %%J in (
  ' gawk.exe "{if(match($0,/ ([0-9]+):miss sMod:(.) /,a)){print(a[1],a[2]);exit}}" %jLst% '
) do (
  set miss=%%J
  set sMod=%%K
)
if NOT defined sMod set errInf=NO 'sMod' key in %jLst% & goto :eof
::  sPre и sSec - старое и новое значения самого позднего времени обновления статистик
set sPre=%sSec%
::  sEnd - дата последнего обновления статистик
set sEnd=
for /F "tokens=1-9,*" %%J in (
'gawk.exe "{if(match($0,/^;sSec:([0-9]+) ([^:]+):sEnd ([^:]+):dEnd ([0-9]+):nEnd ([0-9]+):nBad /,a)){print(a[1],a[2],a[3],a[4],a[5],a[6]);exit}}" %jLst%'
) do (
  set sSec=%%J
  set sEnd=%%K
  set dEnd=%%L
  set nEnd=%%M
  set nBad=%%N
)
if NOT defined sEnd set errInf=NO 'sEnd' key in %jLst%
goto :eof

:getLn %%J
set /a tmp0+=1
set tmp1=%6
set tmp1=%tmp1:~1%
if %tmp0% == 1 set myIp=%3
if %tmp0% == 2 (
  if '%tmp1%' == 'not' (
    set myIp=%myIp% - NO.inSpam
    set /a IpOk+=1
  ) else (
    set myIp=%myIp% ! IS.inSpam
  )
)
if %tmp0% == 3 (
  if '%2' == 'эх' (
    set myIp=%myIp% - NO.inBan
    set /a IpOk+=1
  ) else (
    set myIp=%myIp% ! IS.inBan
  )
)
goto :eof

======= комплект п/п для обустройства заданных ожиданий в CMD
:waitSecs %%A организовать паузу в заданное число секунд /%1/ оживляж с новой строки, если /%2/
call :ticSet
goto sleep6

:ticShow оживляж - это наше всё
gawk.exe "BEGIN{if(%curM%==%preM%){printf(\".\")}else{printf(%curM%)}exit}"
:: строка терминируется 10-й минутой /"0"/
if %curM% == 0 if NOT %curM% == %preM% echo. %curT%
:: проверка истечения заданного срока; "да"==вЫход
if %curS% GEQ %tics% echo. %curT% & goto :eof
:sleep6  шаг задержки - 6 секунд
sleep 6
:: рабочая фиксация очередного момента
call :ticSet
goto ticShow

:ticSet запомним старое значение момента времени относительно начального /jSec/
set preM=%curM%
for /f "tokens=1-9,*" %%P in (
  ' gawk.exe "BEGIN{t=systime();print(t,strftime(\"%%H:%%M:%%S %%M\",t-%jSec%,1));exit}" '
) do (
  set curS=%%P
  set curT=%%Q
  set curM=%%R
)
set curT=+%curT:~1%
:: и текущее /новое/ значение минут /последняя цифра/
set curM=%curM:~1%
goto :eof

:: 2022.04.30, nvv, радикально переделано под единую задачу: "дождаться стабильности в ситуации с обновлением"
:: 2022.05.24, nvv, мелкая доводка и косметика
:: 2022.07.04, nvv, +накопление nvv.stats в #nvv
:: 2022.07.11, nvv, zSchk - ySchk
:: 2022.07.24, nvv, добавлена секция из xIpChk.cmd - проверка своего статуса IP в СИ
:: 2022.08.26, nvv, нет смысла проверять доступ, если работаем с samlib.ru
:: 2022.09.02, nvv, нет смысла проверять доступ, если работаем с samlib.ru
:: 2022.09.30, nvv, add check: а не ЗАБЫЛ ли я ОБИХОДИТЬ прошедщий намедни РАСЧЁТ? /*.dText и *.gAuth/
:: 2022.10.28, nvv, softed check: а не ЗАБЫЛ ли я ОБИХОДИТЬ прошедщий намедни РАСЧЁТ? /*.dText и *.gAuth/
:: 2022.12.20, nvv, updated nvv's stat stars its show up
:: 2022.12.31, nvv, remore nvv's stat check
:: 2023.01.09, nvv, ySchk - zSchk + maxW=200 - maxW=100
:: 2023.06.04, nvv, ожидание через sleep, а не через цикл в gawk + 7:23 - старт НЕ раньше
:: 2023.07.16, nvv, исправлена неточность в сборке списка скачиваемого по aHIts /fHit/

    Имя файла скрипта: "wGetUrl2fil.cmd" V-текст скрипта-V
- - - - - - - - -
@if not defined nJob @echo off
:: установленная переменная nJob (вызов из "моего" батника) блокирует команду echo off
set nReV=2.21, 2023.06.03
if NOT %2.==. goto nStart
:nHelp
echo CmdName: %~nx0 (wGetUrl2fil.cmd)
echo Usage: . %~nx0 iNetUrl_to_downLoad File_for_DL-ed [cmdLog]
echo Example: %~nx0 %siUrl%/n/nosow_w_w n-nosow_w_w.idx.html
echo Example: %~nx0 %siUrl%/ siTitle.idx.html my.log
echo Created: 2018.09.19
echo Author : NVV (Vladislav Nosov)
echo nReV:    %nReV%
echo.
echo Description: ФУНКЦИЯ скачивания файла из интернета в локальный файл
echo   Третий, НЕобязательный ключ задаёт лог +режим отладки для wGet.exe
echo   ^^-без, - режим "всё молча" (для всего)
echo.
echo NB#1. ! В месте размещения юзается времянка %~n0.tmp
echo NB#2. В %%wGetErr%% код ошибки: 0==Ок, 1==no_DL_файла, 3==NO_СИ
echo     + В %%wGetSiz%% - размер DL_файла (File_for_DL) == 0, если оного нет
echo.
echo При возникновении проблемы выставляется ErrorLevel 1,
echo описание проблемы помещается в переменную среды %%errInf%%
echo.
set errInf=It's HELP (params: %*)

:nErrExit
set errInf=! Error in %~nx0:%errInf%
echo %errInf%
exit /b 1

2019.01.14 11:03, nvv, оптимизация алгоритма; по мелочам, но много где...
2019.11.29, nvv, samlib.ru budclub.ru - основной и альтернативный адреса "Самиздата"
2020.01.06, nvv, in errInf "iNet unavailable" reqURL added
2020.03.22, nvv, вместо титула моего раздела проверяется доступность логов СИ (/logs/)
2020.11.13, nvv, будКлуб - глупость
2021.04.27, nvv, ^-не факт + добавлен инкремент внешнего счётчика DnLd при успехе
2021.05.09, nvv, инкремент по-любому (если без ошибок) отсутствие файла по адресу - не ошибка!
2023.06.03, nvv, обустроена работа с wGetErr /см. nHelp/

:nStart
if NOT defined siUrl set siUrl=samlib.ru
if NOT defined wKey set wKey=-U "Mozilla/5.0"
if NOT defined wTim0 set wTim0=%time%
if NOT defined wTim2 set wTim2=%time%
set wGetLog=%3
if defined wGetLog echo {sub} %~nx0 [%*]
:: если заказанное уже у нас, пропускаем (с отметкой)
if NOT exist %2 goto wait0
for %%W in (%2) do set wGetSiz=%%~zW
if defined wGetLog echo == Ok, done already, size=%wGetSiz%
goto :eof

:wait0
set errInf=wGet.exe -q %wKey% %1 -O %2
if defined wGetLog (
  set errInf=wGet.exe %wKey% -d %1 -O %2 -a %3
  echo ? %errInf%
)
:wait1 организация паузы (секунда) на запрос к СИ
if "%wTim0%" GTR "%time%" goto gogo1
if "%wTim2%" GTR "%time%" goto wait1
:gogo1
call :wTim1sec "взвести таймер"
:: собственно скачивание заказанного...
%errInf%
set wGetErr=%errorLevel%
for %%W in (%2) do set wGetSiz=%%~zW
if defined wGetLog echo ~~~ wGetErr=%wGetErr%, size=%wGetSiz%
if %wGetErr% GTR 0 goto chkSi
set errInf=
rem set wGetErr=
:: увеличим счётчик фактов скачиваний
set /a DnLd+=1
goto :eof

коды, которые возвращает wget и пояснения к ним.
0 - ошибок нет
1 - ошибка кода общего характера (generic error code)
2 - ошибка разрбора (parse error) при аналие опций командной строки и/или .wgetrc или .netrc
3 - ошибка файлового ввода/вывода
4 - ошибка сети
5 - ошибка проверки SSL
6 - ошибка аутентификации (неверное имя или пароль)
7 - ошибка протокола
8 - сервер при ответе выдал какую-либо ошибку

:chkSi какие-то проблемы..., а Самиздат-то жив?
set errInf=wGet.exe %wKey% -q %siUrl%/logs -O-
if defined wGetLog (
  echo ? %errInf% to- nul
  set errInf=wGet.exe %wKey% -d -a %3  %siUrl%/logs  -O-
)
rem echo err.%wGetErr% %wGetLog% %errInf%
:wait2 организация паузы (секунда) на запрос к СИ
if "%wTim0%" GTR "%time%" goto gogo2
if "%wTim2%" GTR "%time%" goto wait2
:gogo2 скачать требуемое не удалось, глянем заведомо существующее
call :wTim1sec "взвести таймер"
%errInf% > nul
set wGetChk=%errorLevel%
if defined wGetLog echo ~~~ wGetChk=%wGetChk%
if %wGetChk% == 0 (
if defined wGetLog echo x is gone=%1
set errInf=
set wGetErr=1
goto :eof
)
set wGetErr=3
if exist %2 erase %2
set errInf=%1 is unavailable now...
goto nErrExit

:wTim1sec {sub} взведение таймера на 1 секунду
:: time is like 13:18:24.21 !!! НЕ использовать на ГРАНИЦЕ суток!
set wTim0=%wTim2%
set wTim2=%time%
:: час требует доп.плясок, т.к. при часе <10 в %time% лидирует пробел, а НЕ ноль ("0")
set /a wHou=100+%wTim2:~0,2%
:: CMD чудит на числа с лидирующими нулями... ;(
set wMin=1%wTim2:~3,2%
set wSec=1%wTim2:~6,2%
set /a wSec+=1
if %wSec% GTR 159 (
set /a wSec-=60
set /a wMin+=1
)
if %wMin% GTR 159 (
set /a wMin-=60
set /a wHou+=1
)
:: для часа пробел -v- если <10 (в %time% лидирует пробел {" "}, а НЕ ноль {"0"})
if %wHou% LEQ 109 (set wHou= %wHou:~-1%) else (set wHou=%wHou:~-2%)
:: соберём время == текущий момент + 1 секунда
set wTim2=%wHou%:%wMin:~-2%:%wSec:~-2%%wTim2:~-3%
goto :eof

(copyleft 2018..23 /\/.\/.\/.)

 Ваша оценка:

Связаться с программистом сайта.

Новые книги авторов СИ, вышедшие из печати:
Э.Бланк "Пленница чужого мира" О.Копылова "Невеста звездного принца" А.Позин "Меч Тамерлана.Крестьянский сын,дворянская дочь"

Как попасть в этoт список
Сайт - "Художники" .. || .. Доска об'явлений "Книги"