mdview2: фоновая сборка второго набора кодировки в паузах между клавишами; версия v1.0 (b3)
- индексатор порезан на резюмируемые шаги index_begin/index_step/index_finish; межшаговое состояние в статиках модуля, в docset_t не входит - bg_build_start/bg_step: второй набор (UTF-8 при 8-битном первичном и наоборот) строится в idle главного цикла; холдаун после клавиш, спиннер погашен (g_bg_building) — фон незаметен - F8 до готовности докручивает начатое фоном (ветка resume в build_doc), а не строит заново; общий setup вынесен в doc_setup - кодировка в статус-баре показывается сразу (детект/F8), не дожидаясь конца индексации - побочный фикс: UTF-конвертация впереди проверки останова — >4КБ абзац больше не обрывает конвертацию остатка - README.md (новый, v1.0 b3), CHANGELOG.md; дискета: README/DEMO/CHANGES в трёх кодировках (пути автодетекта) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
# MDView - CHANGELOG
|
||||
|
||||
”®à¬ â: ¢¥àᨨ ¨ ¤ âë ¯® git-¨áâ®à¨¨ ¯à®¥ªâ . ” ©« á®åà ñ ¢ CP866 -
|
||||
¥£® 㤮¡® ç¨â âì á ¬¨¬ MDView (§ ®¤® íâ® â¥á⠢⮤¥â¥ªâ ª®¤¨à®¢ª¨).
|
||||
|
||||
## v1.0 (b3) - 2026-07-05
|
||||
|
||||
- ”®®¢ ï ¯®¤£®â®¢ª ¢â®à®£® ¡®à ª®¤¨à®¢ª¨: ¯®ª ¯®«ì§®¢ ⥫ì ç¨â ¥â
|
||||
¤®ªã¬¥â, ¯ ã§ë ¬¥¦¤ã ª« ¢¨è ¬¨ âà âïâáï ¨¤¥ªá æ¨î ¢â®à®£® ¡®à
|
||||
(UTF-8 ¯à¨ 8-¡¨â®¬ ¯¥à¢¨ç®¬ ¨ ®¡®à®â). Š ¯¥à¢®¬ã F8 ¡®à ®¡ëç®
|
||||
㦥 £®â®¢ - ¯¥à¥ª«î票¥ ¬£®¢¥®. F8 ¤® £®â®¢®á⨠¥ áâந⠡®à
|
||||
§ ®¢®, ¤®ªàã稢 ¥â ç ⮥ ä®®¬ (¦¤ âì ®áâ ñâáï ¬¥ìè¥).
|
||||
- ”® ¯®«®áâìî ¥¢¨¤¨¬: íªà ¥ âண ¥âáï, ¯®á«¥ ª ¦¤®© ª« ¢¨è¨
|
||||
¢ë¤¥à¦¨¢ ¥âáï 宫¤ ã - ¯à¨ áªà®««¥ á ¢â®¯®¢â®à®¬ ä® ¥ ¢ª«¨¨¢ ¥âáï.
|
||||
- ˆ¤¥ªá â®à ¯¥à¥¢¥¤ñ १àã¥¬ë¥ è £¨ (index_begin/index_step);
|
||||
¯®¡®ç® § ªàëâ ªà ©: ®¡àë¢ UTF-ª®¢¥àâ æ¨¨ ᢥà夫¨®¬ (>4 Š�)
|
||||
¡§ æ¥ ¡®«ìè¥ ¥ ®¡à¥§ ¥â ®áâ ⮪ ¤®ªã¬¥â .
|
||||
- Š®¤¨à®¢ª ¢ áâ âãá-¡ ॠ¯®ª §ë¢ ¥âáï áà §ã: ¤¥â¥ªâ¨à®¢ ï - ¢ ¬®¬¥â
|
||||
ç « ¯¥à¢®© ¨¤¥ªá 樨, ®¢ ï ¯® F8 - ¢ ¬®¬¥â ¯¥à¥ª«î票ï, ¥
|
||||
¤®¦¨¤ ïáì ª®æ ᡮન ¡®à .
|
||||
|
||||
## v1.0 (b1) - 2026-07-05
|
||||
|
||||
- �®¢ë© HEX-०¨¬ (F4): ¤ ¬¯ Ž�ˆƒˆ�€‹œ�ŽƒŽ ä ©« ¢ ä®à¬ â¥
|
||||
"0x012340 : 16 ¡ ©â hex : 16 ¯¥ç âëå ᨬ¢®«®¢".
|
||||
�¥ç â ï ª®«®ª ãç¨âë¢ ¥â ⥪ãéãî ª®¤¨à®¢ªã; ¤«ï UTF-8 £«¨ä
|
||||
áâ ¢¨âáï ¯®§¨æ¨¨ «¨¤-¡ ©â , continuation-¡ ©âë - â®çª®©.
|
||||
‘¬¥ ª®¤¨à®¢ª¨ (F8) ¢ HEX ¬¥ï¥â ⮫쪮 ¯¥ç âãî ª®«®ªã.
|
||||
- �பàã⪠HEX ®¤ã áâபã - ¯¯ à âë© áªà®«« á ¯®¤à¨á®¢ª®©
|
||||
®¤®£® àï¤ (ª ª ¢ MD/RAW).
|
||||
- ‘¯à ¢ª : ¤®¡ ¢«¥ ª« ¢¨è F4, ¢¥àá¨ï v1.0 (b1).
|
||||
|
||||
## v1.0 (a4) - 2026-07-04
|
||||
|
||||
- …¤¨ ï ¯®§¨æ¨ï ¯à¨ ¯¥à¥ª«î票ïå ¢¨¤®¢ ¨ ª®¤¨à®¢®ª: F2 (MD/RAW),
|
||||
F8 (8-¡¨âë¥ / UTF-8) á®åà ïîâ ⥪ã饥 ¬¥áâ® ¢ ¤®ªã¬¥â¥.
|
||||
Œ¥¦¤ã ¡®à ¬¨ à §®£® à §¬¥à ¯®§¨æ¨ï ¯¥à¥®á¨âáï ¯à®¯®à樮 «ì®
|
||||
¡ ©â ¬ á â®ç®áâìî ¤® áâப¨.
|
||||
- ˆá¯à ¢«¥®: F8 ¢ RAW-०¨¬¥ à¨á®¢ « MD-¢¨¤ ¯®¢¥àå RAW.
|
||||
- Ž¯â¨¬¨§ æ¨ï à §¬¥à : exe 28215 -> 25100 ¡ ©â (-11%):
|
||||
£à¥áᨢ ï ««®ª æ¨ï ॣ¨áâ஢ SDCC (--max-allocs), § ã«¥¨¥
|
||||
_DATA ¢ crt0 (áïâë ¢á¥ "= 0" ã áâ ⨪®¢), ¤¥¤ã¯«¨ª æ¨ï ¯ àá¥à ,
|
||||
᫨逸 ¤¢ãå ᪠¥à®¢ inline-à §¬¥âª¨ ¢ ®¤¨ scan_stream.
|
||||
|
||||
## v1.0 (a3) - 2026-06-30
|
||||
|
||||
- ‹¨¬¨â ä ©« 256 Š�: ¡®«ì訩 ä ©« ®¡à¥§ ¥âáï á ¯à¥¤ã¯à¥¦¤¥¨¥¬,
|
||||
¯à®ç¨â ï ç áâì ¤®áâ㯠.
|
||||
- Ž¡àë¢ ¯® ¨áç¥à¯ ¨î ¨¤¥ªá /ªíè § ¢¥àè ¥âáï áâப®©-á®®¡é¥¨¥¬
|
||||
á ¯à¨ç¨®© (¢¬¥áâ® â¨å®© ¯®â¥à¨ áâப).
|
||||
- �®«¨à®¢ª áâ âãá-¡ à ¨ á¯à ¢ª¨; ¯à®æ¥â ¯à®ªàã⪨ 16-¡¨â®©
|
||||
à¨ä¬¥â¨ª¥ (¡¥§ 32-¡¨â®£® ¤¥«¥¨ï).
|
||||
- ’¥áâ®¢ë¥ ä ©«ë £à ¨çëå á«ãç ¥¢ (testfiles/, £¥¥à â®à).
|
||||
|
||||
## v1.0 (a2) - 2026-06-26
|
||||
|
||||
- Œ®®«¨â à á饯«ñ ¬®¤ã«¨: ï¤à® + index/md/raw/enc/table/status/help
|
||||
(à §¬¥à-¥©âà «ì®, ç¨â ¥¬®áâì).
|
||||
- ˆá¯à ¢«¥ ¤ã¡«¨ª â áâப¨ ¢ RAW ¯à¨ áªà®««¥ ã ª®æ ä ©« .
|
||||
|
||||
## v1.0 (a1) - 2026-06-25
|
||||
|
||||
- �®¤¤¥à¦ª ª®¤¨à®¢®ª CP866/CP1251/KOI8-R/UTF-8 (F8):
|
||||
¢â®®¯à¥¤¥«¥¨¥ ¯à¨ ®âªàë⨨, ¬£®¢¥ë© ६ ¯ 8-¡¨âëå,
|
||||
UTF-8 ¤¥ª®¤¨àã¥âáï ¢ ®â¤¥«ìë© ¡®à (¨ªà¥¬¥â «ì®).
|
||||
- RAW-¯à®á¬®âà ¨á室¨ª (F2): ¯¥à¥®á ªà â® 80 ¨«¨ ¯ ®à ¬ (F3).
|
||||
- Ž¯â¨¬¨§ æ¨ï à §¬¥à ª®¤ (-1.9 Š�).
|
||||
|
||||
## v1.0 (a0) - 2026-06-23..24
|
||||
|
||||
- �¥à¢ë© ¢ë¯ãáª: render-cache àå¨â¥ªâãà - ®¤¨ ¯à®å®¤ ¯ àá¥à ,
|
||||
£®â®¢ë¥ áâப¨ (ᨬ¢®«+ âਡãâ) ¢ EMM, ¬£®¢¥ ï ¯à®ªàã⪠.
|
||||
- Markdown: § £®«®¢ª¨, ᯨ᪨, æ¨â âë, à §¤¥«¨â¥«¨, fenced-ª®¤,
|
||||
â ¡«¨æë á à ¬ª®© ¨ ¢â®-è¨à¨®© ª®«®®ª, inline-à §¬¥âª
|
||||
(¦¨àë©/ªãàᨢ/¯®¤çñàªãâë©/§ çñàªãâë©/ª®¤, íªà ¨à®¢ ¨¥).
|
||||
- �ணà¥áᨢ ï § £à㧪 : ¯¥à¢ë© íªà áà §ã, ¨¤¥ªá æ¨ï ¢ 䮥,
|
||||
¢¨£ æ¨ï ¯® £®â®¢®© ç áâ¨, Esc/F10 ¯à¥àë¢ ¥â.
|
||||
- ƒ®à¨§®â «ìë© áªà®«« ª®¤®¢ëå ¡«®ª®¢ ¨ â ¡«¨æ.
|
||||
- “᪮२¥ ¯à¥¯à®æ¥áᨣ (22á -> 10á ¡®«ì讬 ä ©«¥).
|
||||
+20
-19
@@ -17,35 +17,36 @@ EXTRA_FLAGS := --max-allocs 100000
|
||||
include $(PROJ_ROOT)/app.mk
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Образ дискеты: только mdview2.exe + README.MD (перекодированный
|
||||
# из UTF-8 в CP866 — рабочую кодировку Sprinter).
|
||||
#
|
||||
# README.MD хранится в репозитории в UTF-8; iconv -c конвертирует
|
||||
# его в CP866, отбрасывая символы без аналога в целевой кодировке.
|
||||
# Результат кладётся в .disk_tmp/README.MD, чтобы make_disk.py
|
||||
# использовал правильное имя файла на диске.
|
||||
# Образ дискеты: mdview2.exe + документация. Каждый документ — в СВОЕЙ
|
||||
# кодировке, чтобы дискета заодно покрывала все пути автодетекта:
|
||||
# README.MD — документация, ОРИГИНАЛЬНЫЙ UTF-8 (как в репозитории);
|
||||
# CHANGES.MD — история версий, CP866 (хранится так, кладётся как есть;
|
||||
# имя укорочено: "CHANGELOG" не влезает в FAT 8.3);
|
||||
# DEMO.MD — демонстрация разметки, конвертируется UTF-8 → CP1251.
|
||||
#
|
||||
# iconv -c возвращает ненулевой код, если хоть один символ отброшен
|
||||
# (даже с -c) — это ОЖИДАЕМО при потере символов без аналога в CP866,
|
||||
# не ошибка конвертации; вывод при этом всё равно корректно записан.
|
||||
# (даже с -c) — это ОЖИДАЕМО при потере символов без аналога в целевой
|
||||
# кодировке, не ошибка конвертации; вывод при этом корректно записан.
|
||||
# Поэтому код возврата iconv игнорируется (|| true).
|
||||
#
|
||||
# Тестовые файлы лимита 256 КБ лежат в testfiles/ (генератор gen_testfiles.py);
|
||||
# на диск НЕ кладутся — при необходимости добавить вручную в список ниже.
|
||||
# ------------------------------------------------------------------
|
||||
DISK_TMP := .disk_tmp
|
||||
README_DISK := $(DISK_TMP)/README.MD
|
||||
DEMO_DISK := $(DISK_TMP)/DEMO.MD
|
||||
CHLOG_DISK := $(DISK_TMP)/CHANGES.MD
|
||||
|
||||
$(DISK_TMP):
|
||||
mkdir -p $@
|
||||
|
||||
$(README_DISK): README.MD | $(DISK_TMP)
|
||||
iconv -c -f UTF-8 -t CP866 README.MD > $@ || true
|
||||
$(CHLOG_DISK): CHANGELOG.md | $(DISK_TMP)
|
||||
cp CHANGELOG.md $@
|
||||
|
||||
# UTF8TEST.MD кладётся на диск КАК ЕСТЬ (в UTF-8, без перекодировки) —
|
||||
# это тестовый вход для проверки UTF-8 рендеринга (Фаза 2 кодировок).
|
||||
#
|
||||
# Тестовые файлы лимита 256 КБ лежат в testfiles/ (генератор gen_testfiles.py);
|
||||
# на диск НЕ кладутся — при необходимости добавить вручную в список ниже.
|
||||
floppy: $(EXAMPLE).exe $(README_DISK)
|
||||
python3 $(MAKE_DISK) $(FLOPPY_IMG) $(EXAMPLE).exe $(README_DISK) UTF8TEST.MD
|
||||
$(DEMO_DISK): DEMO.MD | $(DISK_TMP)
|
||||
iconv -c -f UTF-8 -t CP1251 DEMO.MD > $@ || true
|
||||
|
||||
floppy: $(EXAMPLE).exe $(DEMO_DISK) $(CHLOG_DISK)
|
||||
python3 $(MAKE_DISK) $(FLOPPY_IMG) $(EXAMPLE).exe README.md $(DEMO_DISK) $(CHLOG_DISK)
|
||||
@echo
|
||||
@echo "Floppy ready: $(FLOPPY_IMG)"
|
||||
@echo "Run: cd $(MAME_DIR) && ./run_mame.sh"
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
# MDView v1.0 (b3) — просмотрщик Markdown для Sprinter
|
||||
|
||||
**MDView** — программа просмотра документов Markdown для компьютера
|
||||
**Sprinter-2000** (Z80, ОС ESTEX). Файл загружается в расширенную память (EMM)
|
||||
и один раз «прогоняется» через парсер: готовые к показу строки (пары
|
||||
символ+атрибут) складываются в **рендер-кэш** в EMM, после чего прокрутка в
|
||||
любую сторону — это просто копирование готовых строк на экран, без повторного
|
||||
парсинга. Даже на файлах в сотни килобайт листание остаётся мгновенным.
|
||||
|
||||
Текстовый режим 80×32, цветное оформление элементов разметки, три режима
|
||||
просмотра (MD / RAW / HEX) и четыре кодировки с автоопределением.
|
||||
|
||||
---
|
||||
|
||||
## Возможности
|
||||
|
||||
* **Markdown-рендеринг** с цветовым оформлением:
|
||||
* заголовки `#`…`######` (H5/H6 отображаются как H4);
|
||||
* **жирный** (`**текст**`), *курсив* (`*текст*`), подчёркнутый (`_текст_`),
|
||||
~~зачёркнутый~~ (`~~текст~~`), `встроенный код` (`` `текст` ``);
|
||||
* экранирование `\*`, `\_`, `` \` `` и любой ASCII-пунктуации;
|
||||
* чекбоксы `[x]` / `[ ]` в списках;
|
||||
* ненумерованные (`-`, `*`, `+`) и нумерованные (`1.`, `1)`) списки
|
||||
с базовой вложенностью по отступам;
|
||||
* цитаты `>` (склейка многострочных, маркер │ на переносах);
|
||||
* fenced-блоки кода ` ``` ` (без переносов, горизонтальный скролл);
|
||||
* таблицы `| … | … |` — рисуются псевдографической рамкой, ширины колонок
|
||||
вычисляются по содержимому (до 16 колонок);
|
||||
* горизонтальные разделители `---` / `***` / `___`;
|
||||
* жёсткие переносы (два пробела или `\` в конце строки);
|
||||
* мягкая склейка абзацев с переносом по словам под ширину экрана.
|
||||
* **Кодировки: CP866, CP1251, KOI8-R, UTF-8.**
|
||||
* автоопределение при открытии (BOM → UTF-8; валидность multibyte-структуры;
|
||||
частотный анализ ходовых русских букв для 8-битных);
|
||||
* переключение по кругу клавишей **F8** в любой момент;
|
||||
* 8-битные кодировки отличаются только перекодировкой глифов на отрисовке —
|
||||
переключение мгновенно;
|
||||
* UTF-8 декодируется в CP866 в **отдельный набор** (файл + индекс + кэш);
|
||||
второй набор готовится **в фоне**, пока вы читаете документ, — обычно
|
||||
к первому нажатию F8 он уже построен и переключение мгновенно,
|
||||
с сохранением позиции. Если фон не успел, F8 докручивает начатую
|
||||
сборку (со спиннером), а не начинает её заново.
|
||||
* **Три режима просмотра:**
|
||||
* **MD** — форматированный Markdown (по умолчанию);
|
||||
* **RAW** (**F2**) — исходный текст без разметки: перенос строк кратно 80
|
||||
(**F3** — режим панорамы с горизонтальным скроллом);
|
||||
* **HEX** (**F4**) — дамп *оригинального* файла:
|
||||
`0x012340 │ 16 байт hex │ 16 печатных символов`. Печатная колонка
|
||||
интерпретируется текущей кодировкой; для UTF-8 глиф ставится на позиции
|
||||
лид-байта, continuation-байты показываются точкой.
|
||||
* **Единая позиция** при любых переключениях: MD ↔ RAW ↔ HEX и смена
|
||||
кодировки сохраняют текущее место в документе (между наборами разного
|
||||
размера — пропорционально, с точностью до строки).
|
||||
* **Прогрессивная загрузка**: первый экран показывается сразу, индексация
|
||||
продолжается в фоне; по готовой части документа уже можно листать,
|
||||
**Esc**/**F10** прерывают загрузку.
|
||||
* **Фоновая работа незаметна**: второй набор кодировки строится только в
|
||||
паузах между клавишами (после нажатия выдерживается пауза), поэтому
|
||||
скролл — в том числе с автоповтором — не теряет плавности.
|
||||
|
||||
---
|
||||
|
||||
## Запуск
|
||||
|
||||
```
|
||||
MDVIEW2.EXE <файл.md>
|
||||
```
|
||||
|
||||
Без аргумента открывается `README.MD` из текущего каталога.
|
||||
|
||||
## Клавиши
|
||||
|
||||
| Клавиша | Действие |
|
||||
|--------------|-------------------------------------------------------------|
|
||||
| ↑ / ↓ | прокрутка на одну строку |
|
||||
| PgUp / PgDn | прокрутка на экран (30 строк) |
|
||||
| Home / End | в начало / в конец документа |
|
||||
| ← / → | горизонтальный сдвиг: код/таблицы в MD, панорама в RAW |
|
||||
| F1 | справка |
|
||||
| F2 | RAW-режим ↔ MD |
|
||||
| F3 | в RAW: перенос строк ↔ панорама |
|
||||
| F4 | HEX-режим ↔ прежний вид |
|
||||
| F8 | кодировка: CP866 → CP1251 → KOI8-R → UTF-8 → … |
|
||||
| Esc / F10 | выход (во время загрузки — прервать её) |
|
||||
|
||||
Статус-бар (верхняя строка): имя файла, кодировка, диапазон видимых строк
|
||||
и процент прокрутки. Нижняя строка — меню доступных F-клавиш.
|
||||
|
||||
---
|
||||
|
||||
## Ограничения
|
||||
|
||||
| Параметр | Значение |
|
||||
|---------------------------------|-------------------------------------------|
|
||||
| Размер файла | до 256 КБ (больший — обрезается с предупреждением) |
|
||||
| Логических строк (после переносов) | до 18 432 |
|
||||
| Длина строки в рендер-кэше | 255 ячеек (RAW/HEX ограничения не имеют) |
|
||||
| Колонок в таблице | до 16 |
|
||||
| Шаг табуляции | 4 |
|
||||
|
||||
При исчерпании любого лимита документ завершается строкой-сообщением
|
||||
с указанием причины обрыва; всё, что вошло, доступно для просмотра.
|
||||
|
||||
## Требования
|
||||
|
||||
* Sprinter-2000 с ОС ESTEX;
|
||||
* расширенная память (EMM): в худшем случае (файл 256 КБ + оба набора
|
||||
кодировок) — до ~150 страниц по 16 КБ (~2,4 МБ). Для типичных файлов
|
||||
в десятки килобайт достаточно нескольких десятков страниц.
|
||||
|
||||
Код, данные, стек и куча программы занимают окна W1+W2 (32 КБ, режим
|
||||
памяти `small`); окно W3 используется только для доступа к EMM-страницам.
|
||||
|
||||
---
|
||||
|
||||
## Сборка
|
||||
|
||||
Требуется тулчейн этого репозитория (обёртка `sprinter-cc` над SDCC 4.5).
|
||||
Из каталога `examples/mdview2`:
|
||||
|
||||
```
|
||||
make # собрать mdview2.exe
|
||||
make floppy # собрать и упаковать дискету для MAME (mc.img)
|
||||
make run # floppy + запуск MAME
|
||||
```
|
||||
|
||||
На дискету кладутся: `MDVIEW2.EXE` и три документа, каждый в своей кодировке
|
||||
(заодно покрывают все пути автодетекта): `README.MD` — этот файл, как есть
|
||||
(UTF-8); `DEMO.MD` — демонстрация всех элементов разметки (CP1251);
|
||||
`CHANGES.MD` — история версий (CP866).
|
||||
|
||||
## Структура исходников
|
||||
|
||||
| Файл | Назначение |
|
||||
|------------------|--------------------------------------------------------------|
|
||||
| `mdview2.c` | ядро: EMM-аллокации, рендер-кэш, наборы кодировок, загрузка файла, главный цикл |
|
||||
| `mdview2_index.c`| парсер/индексатор Markdown — единственный проход по файлу |
|
||||
| `mdview2_md.c` | MD-вид: отрисовка из кэша, прокрутка |
|
||||
| `mdview2_raw.c` | RAW-вид (F2/F3) |
|
||||
| `mdview2_hex.c` | HEX-вид (F4) |
|
||||
| `mdview2_enc.c` | кодировки: детект, ремап-таблицы, конвертер UTF-8 → CP866 |
|
||||
| `mdview2_table.c`| отрисовка таблиц |
|
||||
| `mdview2_status.c`| статус-бар, меню, спиннер |
|
||||
| `mdview2_help.c` | справка (F1) |
|
||||
| `mdview2_conf.h` | конфигурация: `WITH_RAW` / `WITH_HEX` (модули отключаемы) |
|
||||
| `mdview2.h` | общие константы, атрибуты, межмодульный API |
|
||||
|
||||
Подробности архитектуры — в `docs/mdview2-plan.md`.
|
||||
|
||||
## Лицензия и авторы
|
||||
|
||||
© 2026 Петров А.Г. Часть проекта Sprinter C Compiler
|
||||
(см. LICENSE в корне репозитория).
|
||||
+125
-16
@@ -203,10 +203,12 @@ static uint8_t g_raw_last = VIEW_RAW_WRAP; /* последний под
|
||||
static uint8_t g_hex_last = VIEW_MD; /* вид, из которого вошли в HEX (возврат по F4) */
|
||||
#endif
|
||||
#endif
|
||||
uint8_t g_loading; /* extern: 1 во время index_lines() */
|
||||
uint8_t g_loading; /* extern: 1 во время видимой индексации (build_doc) */
|
||||
static uint8_t g_first_drawn; /* первый экран уже нарисован */
|
||||
uint8_t g_f8_enabled; /* extern: можно ли сейчас переключать кодировку */
|
||||
static uint8_t g_utf_failed; /* UTF-8 набор не удалось построить (нет EMM) — пропускаем UTF-8 в цикле F8 */
|
||||
uint8_t g_bg_building; /* extern: 1 во время фонового index_step — гасит спиннер индексатора */
|
||||
static uint8_t g_bg_doc = 0xFF; /* слот, строящийся в фоне (bg_step); 0xFF = фоновой сборки нет */
|
||||
uint8_t g_abort; /* extern: F10/Esc во время загрузки → прервать индексацию */
|
||||
static uint16_t g_last_prog; /* n_lines на последнем обновлении статуса */
|
||||
|
||||
@@ -768,26 +770,34 @@ void progress_tick(void)
|
||||
* Точка входа
|
||||
* ================================================================== */
|
||||
|
||||
/* Строит набор `slot` кооперативно (index_lines + progress_tick).
|
||||
/* Строит набор `slot` кооперативно (index_step + progress_tick).
|
||||
* visible=1 — первичный (показываемый): прогрессивная отрисовка первого экрана
|
||||
* и скролл по готовым строкам. visible=0 — вторичный: index_lines строит его в
|
||||
* живых глобалах, а progress_tick через свап показывает/листает уже готовый
|
||||
* и скролл по готовым строкам. visible=0 — вторичный: индексация идёт в живых
|
||||
* глобалах, а progress_tick через свап показывает/листает уже готовый
|
||||
* g_disp_doc. Возврат: 0 — успех, <0 — нехватка EMM (-5/-6/-7).
|
||||
*
|
||||
* Если slot прямо сейчас строится ФОНОМ (g_bg_doc, см. bg_step ниже) —
|
||||
* не начинаем заново, а ДОКРУЧИВАЕМ: загружаем частично построенный набор
|
||||
* и продолжаем с сохранённой позиции индексатора (его межшаговое состояние
|
||||
* живёт в статиках модуля index и переживает свапы наборов).
|
||||
*
|
||||
* UTF-8 набор конвертируется ИНКРЕМЕНТАЛЬНО: выделяем страницы, переключаем
|
||||
* живой буфер на utf и запускаем конвертацию первого чанка; дальше index_lines
|
||||
* живой буфер на utf и запускаем конвертацию первого чанка; дальше index_step
|
||||
* докручивает её впереди позиции чтения (g_utf_building). Конвертер читает
|
||||
* оригинал из orig_file_phys[] через cv_read (не через fb, который теперь
|
||||
* указывает на utf). */
|
||||
static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible)
|
||||
/* Настраивает живые глобалы под сборку набора slot: файловый буфер (utf/orig),
|
||||
* индекс + директория кэша, сброс индексатора. Общая часть build_doc и
|
||||
* bg_build_start. Возврат: 0 — успех, <0 — нехватка EMM (-5/-6/-7). */
|
||||
static int doc_setup(uint8_t slot)
|
||||
{
|
||||
if (slot == DOC_UTF8) {
|
||||
if (!utf_avail) { /* первый вход: выделить страницы */
|
||||
if (!utf_alloc())
|
||||
if (!utf_alloc())
|
||||
return -7; /* выделяет страницы + сбрасывает конвертер */
|
||||
utf_avail = 1;
|
||||
}
|
||||
file_blk = utf_blk;
|
||||
file_blk = utf_blk;
|
||||
file_pages = utf_pages;
|
||||
memcpy(file_phys, utf_phys, MAX_PAGES);
|
||||
if (g_utf_building) {
|
||||
@@ -798,29 +808,45 @@ static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible)
|
||||
file_size = utf_size;
|
||||
}
|
||||
} else {
|
||||
file_blk = orig_file_blk;
|
||||
file_pages = orig_file_pages;
|
||||
file_blk = orig_file_blk;
|
||||
file_pages = orig_file_pages;
|
||||
file_size = orig_file_size;
|
||||
memcpy(file_phys, orig_file_phys, MAX_PAGES);
|
||||
}
|
||||
cur_page = 0xFF;
|
||||
|
||||
int rc = alloc_set_storage();
|
||||
if (rc < 0)
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
index_begin();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible)
|
||||
{
|
||||
if (g_bg_doc == slot) { /* фон уже строит его — продолжить */
|
||||
doc_load_live(slot);
|
||||
g_bg_doc = 0xFF;
|
||||
} else {
|
||||
int rc = doc_setup(slot);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
set_encoding(enc);
|
||||
top_line = 0;
|
||||
top_line = 0;
|
||||
viewport_x = 0;
|
||||
g_active_doc = slot;
|
||||
if (visible) {
|
||||
g_disp_doc = slot;
|
||||
g_first_drawn = 0;
|
||||
if (visible) {
|
||||
g_disp_doc = slot;
|
||||
g_first_drawn = 0;
|
||||
status_encoding(); /* показать кодировку сразу (детект/F8), не дожидаясь индексации */
|
||||
cur_page = 0xFF; /* BIOS-вывод мог сбить W3-маппинг, на который опирается fb() */
|
||||
}
|
||||
|
||||
g_loading = 1;
|
||||
g_last_prog = 0;
|
||||
index_lines();
|
||||
while (!index_step(255)) {}
|
||||
g_loading = 0;
|
||||
|
||||
/* Зафиксировать конвертацию (на случай обрыва индекса по max_lines до конца
|
||||
@@ -835,6 +861,69 @@ static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ==================================================================
|
||||
* Фоновая сборка второго набора (idle-time, между клавишами)
|
||||
* ==================================================================
|
||||
* Пока пользователь читает документ, главный цикл в паузах между клавишами
|
||||
* докручивает индексацию второго набора (UTF-8 при 8-битном первичном и
|
||||
* наоборот) порциями index_step: свап на bg-набор → несколько итераций →
|
||||
* свап обратно. Экран при этом не трогается (g_bg_building гасит спиннер),
|
||||
* задержка реакции на клавишу — не больше одного шага. По готовности F8
|
||||
* переключает наборы мгновенно; F8 ДО готовности докручивает сборку видимо
|
||||
* (ветка resume в build_doc), не начиная её заново. */
|
||||
|
||||
#define BG_STEP_BUDGET 2u /* итераций индексатора на idle-шаг: латентность клавиши
|
||||
* = длительность шага (~единицы мс на обычном абзаце) */
|
||||
#define BG_KEY_HOLDOFF 300u /* пустых опросов kbhit после клавиши до возобновления
|
||||
* фона — при автоповторе (скролл) фон не вклинивается */
|
||||
|
||||
/* Готовит фоновую сборку второго набора: настраивает живые глобалы (буфер,
|
||||
* индекс, кэш, index_begin) так же, как build_doc, но БЕЗ индексации, и
|
||||
* возвращает живым прежний набор. При нехватке EMM фон просто не стартует —
|
||||
* остаётся сегодняшний ленивый путь по F8. */
|
||||
static void bg_build_start(void)
|
||||
{
|
||||
uint8_t slot = (uint8_t)((g_active_doc == DOC_8BIT) ? DOC_UTF8 : DOC_8BIT);
|
||||
uint8_t vis = g_active_doc;
|
||||
if (g_doc[slot].built || (slot == DOC_UTF8 && g_utf_failed))
|
||||
return; /* строить нечего */
|
||||
|
||||
doc_save_live(vis);
|
||||
if (doc_setup(slot) < 0) { /* нет EMM — как и в ленивом пути */
|
||||
if (slot == DOC_UTF8) g_utf_failed = 1;
|
||||
doc_load_live(vis);
|
||||
return;
|
||||
}
|
||||
top_line = 0;
|
||||
viewport_x = 0;
|
||||
|
||||
g_active_doc = slot; /* живые глобалы = bg-набор... */
|
||||
doc_switch(vis); /* ...сохранить его и вернуть видимый */
|
||||
g_bg_doc = slot;
|
||||
}
|
||||
|
||||
/* Один idle-шаг фона: свап на строящийся набор, порция индексации, свап
|
||||
* обратно. Межшаговое состояние индексатора/UTF-конвертера — статики их
|
||||
* модулей, обработчики клавиш видимого набора их не трогают. По завершении
|
||||
* финализирует набор (дальше F8 переключается мгновенно). */
|
||||
static void bg_step(void)
|
||||
{
|
||||
uint8_t vis = g_active_doc;
|
||||
doc_switch(g_bg_doc);
|
||||
g_bg_building = 1;
|
||||
uint8_t done = index_step(BG_STEP_BUDGET);
|
||||
g_bg_building = 0;
|
||||
if (done) {
|
||||
if (g_utf_building) { /* как build_doc: обрыв индекса до конца исходника */
|
||||
utf_size = file_size;
|
||||
g_utf_building = 0;
|
||||
}
|
||||
g_doc[g_bg_doc].built = 1; /* doc_save_live поле built не трогает */
|
||||
g_bg_doc = 0xFF;
|
||||
}
|
||||
doc_switch(vis);
|
||||
}
|
||||
|
||||
/* Пропорциональный перенос байт-позиции между буферами разного размера
|
||||
* (8bit ↔ UTF-8 при F8): off * to / from БЕЗ __mullong/__divulong. Один
|
||||
* цикл restoring-деления: бит k дроби off/from (сравнение с from/2^k)
|
||||
@@ -1079,7 +1168,27 @@ int main(int argc, char **argv)
|
||||
render_full_status();
|
||||
draw_viewport_from_cache();
|
||||
|
||||
/* Второй набор (UTF-8 при 8-битном первичном и наоборот) готовим фоном
|
||||
* в паузах между клавишами — к первому F8 он обычно уже построен. */
|
||||
bg_build_start();
|
||||
|
||||
for (;;) {
|
||||
/* Idle-фон: пока второй набор не достроен, вместо блокирующего getkey
|
||||
* опрашиваем клавиатуру и тратим паузы на bg_step. Холдаун после
|
||||
* каждой клавиши: при активном скролле (автоповтор) фон молчит и
|
||||
* плавность не страдает. После достройки — снова блокирующий getkey. */
|
||||
if (g_bg_doc != 0xFF) {
|
||||
uint16_t idle = 0;
|
||||
while (!kbhit()) {
|
||||
if (idle < BG_KEY_HOLDOFF) {
|
||||
idle++;
|
||||
continue;
|
||||
}
|
||||
bg_step();
|
||||
if (g_bg_doc == 0xFF)
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint16_t k = getkey();
|
||||
uint8_t ascii = (uint8_t)(k & 0xFF);
|
||||
uint8_t scan = (uint8_t)((k >> 8) & 0x7F);
|
||||
|
||||
@@ -245,7 +245,14 @@ uint8_t cache_reserve(uint16_t nbytes, uint8_t *out_page, uint16_t *out_off);
|
||||
void cache_commit(uint8_t page, uint16_t off, const void *buf, uint16_t len);
|
||||
void cache_dir_put(uint16_t idx, const cache_rec_t *r);
|
||||
void progress_tick(void); /* кооперативный шаг loading-loop (рисует/листает) */
|
||||
void index_lines(void); /* построить индекс по загруженному файлу */
|
||||
|
||||
/* Резюмируемая индексация (для фоновой сборки второго набора): begin сбрасывает
|
||||
* проход, step выполняет до budget итераций (блоков исходника) и возвращает 1,
|
||||
* когда индекс финализирован. Межшаговое состояние — статики модуля index;
|
||||
* оно не входит в docset_t (см. bg_step ядра). */
|
||||
void index_begin(void);
|
||||
uint8_t index_step(uint8_t budget);
|
||||
extern uint8_t g_bg_building; /* 1 = фоновый шаг индексации: не трогать экран (спиннер) */
|
||||
|
||||
/* ================================================================== *
|
||||
* Буфер ячеек рендера (mdview2.c) — общий с модулем таблиц.
|
||||
|
||||
@@ -73,7 +73,7 @@ void show_help(void)
|
||||
/* Содержимое (20 внутренних строк) */
|
||||
uint8_t r = 0;
|
||||
help_line(r++, "", ATTR_HELP_BG);
|
||||
help_line(r++, " MDView v1.0 (b1) -- Markdown Viewer for Sprinter", ATTR_HELP_HDR);
|
||||
help_line(r++, " MDView v1.0 (b3) -- Markdown Viewer for Sprinter", ATTR_HELP_HDR);
|
||||
help_line(r++, " (c) 2026 \x8F\xA5\xE2\xE0\xAE\xA2 \x80\x2E\x83\x2E",
|
||||
ATTR_HELP_BG);
|
||||
help_line(r++, "", ATTR_HELP_BG);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* mdview2_index.c — парсер/индексатор markdown (сердце приложения).
|
||||
*
|
||||
* Единственный проход по исходному файлу (index_lines): классификация строк
|
||||
* Единственный проход по исходному файлу (index_begin/index_step): классификация строк
|
||||
* (classify_line, is_*_raw), сборка абзацев с переносом (scan_join_stream),
|
||||
* inline-разметка (inline_marker/inline_scan) и эмиссия готовых (char,attr)-
|
||||
* ячеек в рендер-кэш (gc_put → seg_commit_cells → cache_*). Ведёт индекс
|
||||
@@ -669,31 +669,88 @@ static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
||||
|
||||
/* Отрисовка таблиц (row_end/table_*) — в модуле mdview2_table.c. */
|
||||
|
||||
/* Строит индекс сегментов рендера по загруженному файлу. */
|
||||
void index_lines(void)
|
||||
/* ================== Резюмируемый проход индексации =======================
|
||||
* Бывший монолит index_lines() порезан на begin/step/finish, чтобы фоновая
|
||||
* сборка второго набора (bg_step ядра) могла выполнять индексацию порциями
|
||||
* в паузах между клавишами. Всё межшаговое состояние — в статиках модуля
|
||||
* (idx_p/idx_in_block ниже + g_cells/cur_rec/g_trunc_* выше); оно НЕ входит
|
||||
* в docset_t и переживает свапы наборов, потому что обработчики клавиш
|
||||
* видимого набора индексатор не трогают. */
|
||||
static uint32_t idx_p; /* позиция чтения исходника между шагами */
|
||||
static uint8_t idx_in_block; /* внутри fenced code-блока */
|
||||
static uint8_t idx_active; /* 1 = проход начат и ещё не финализирован */
|
||||
|
||||
/* Начинает новый проход (сброс позиции/буферов/причин обрыва). */
|
||||
void index_begin(void)
|
||||
{
|
||||
uint8_t in_block = 0;
|
||||
idx_p = 0;
|
||||
idx_in_block = 0;
|
||||
n_lines = 0;
|
||||
index_truncated = 0;
|
||||
g_trunc_cause = 0;
|
||||
g_trunc_line = 0;
|
||||
cur_seg_off = 0xFFFFFFFFu; /* пока не эмитили ни одного сегмента */
|
||||
if (file_size == 0) return;
|
||||
g_ncells = 0;
|
||||
g_ncells_at_space = 0;
|
||||
g_skip_flush = 0;
|
||||
idx_active = 1;
|
||||
}
|
||||
|
||||
uint32_t p = 0;
|
||||
while (p < file_size && n_lines < max_lines && !g_abort && !g_trunc_cause) {
|
||||
/* Хвост прохода: финальный флаш ячеек + строка-обрыв (бывший финал index_lines). */
|
||||
static void index_finish(void)
|
||||
{
|
||||
idx_active = 0;
|
||||
/* emit_seg() флашит ячейки с отставанием на одну строку (см. её
|
||||
* комментарий) — последняя эмитированная строка файла так и остаётся
|
||||
* незафлашенной, её ячейки ещё в g_cells; коммитим здесь. */
|
||||
if (n_lines > 0) seg_commit_cells((uint16_t)(n_lines - 1));
|
||||
|
||||
/* UTF-8: держим конвертацию впереди позиции чтения (включая look-ahead
|
||||
* склейки/классификации ~ один абзац). file_size при этом = доступная
|
||||
* сконвертированная часть и растёт по ходу. */
|
||||
if (g_utf_building && (uint32_t)(p + CONV_MARGIN) > file_size)
|
||||
utf_convert_more((uint32_t)(p + CONV_MARGIN));
|
||||
/* Лимит строк ловим ЗДЕСЬ, а не в emit_seg: главный цикл выходит по
|
||||
* `n_lines < max_lines` ДО того, как emit_seg будет вызван в переполненном
|
||||
* состоянии (для code-block — один emit_seg на строку, эта ветка не
|
||||
* достигается). Признак — остановились, а файл не кончился; g_abort и
|
||||
* исчерпание контента (TRUNC_CONTENT) уже обработаны/исключены. */
|
||||
if (!g_trunc_cause && !g_abort && idx_p < file_size) {
|
||||
g_trunc_cause = TRUNC_LINES;
|
||||
g_trunc_line = (uint16_t)(max_lines - 1);
|
||||
}
|
||||
|
||||
if (g_loading)
|
||||
progress_tick(); /* первый экран + статус + скролл по готовым данным */
|
||||
else if ((n_lines & 15) == 0)
|
||||
spinner_tick();
|
||||
/* Файл был обрезан до 256 КБ при загрузке, а индексация дошла до конца
|
||||
* клампнутых данных без исчерпания контента/строк → ДОПИСЫВАЕМ строку
|
||||
* (место есть: n_lines < max_lines). Приоритет ниже content/lines —
|
||||
* те означают, что обрезали ещё раньше, их сообщение точнее. */
|
||||
if (!g_trunc_cause && !g_abort && g_file_clamped) {
|
||||
g_trunc_cause = TRUNC_FILE;
|
||||
g_trunc_line = n_lines;
|
||||
}
|
||||
|
||||
/* Обрыв по контенту/лимиту строк (не путать с пользовательским g_abort):
|
||||
* документ заканчиваем строкой-сообщением на g_trunc_line. Сообщение
|
||||
* рисуется вживую (IF_TRUNC_MSG) — место в контент-кэше ему не нужно, что
|
||||
* и снимает тупик «кэш полон, а сообщение тоже надо закэшировать». */
|
||||
if (g_trunc_cause && !g_abort) {
|
||||
cache_rec_t r;
|
||||
r.page = 0; r.off = 0; r.len = 0;
|
||||
r.flags = IF_TRUNC_MSG; r.reserved = g_trunc_cause;
|
||||
r.pad[0] = 0; r.pad[1] = 0;
|
||||
cache_dir_put(g_trunc_line, &r);
|
||||
n_lines = (uint16_t)(g_trunc_line + 1); /* документ кончается на строке-обрыве */
|
||||
}
|
||||
}
|
||||
|
||||
/* Одна итерация бывшего главного цикла index_lines(): классифицирует и
|
||||
* эмитит очередной блок исходника (параграф/строку кода/таблицу целиком).
|
||||
* do{}while(0) сохраняет исходные `continue` тела как выход из итерации
|
||||
* с записью локальной p обратно в idx_p. */
|
||||
static void index_iter(void)
|
||||
{
|
||||
if (g_loading)
|
||||
progress_tick(); /* первый экран + статус + скролл по готовым данным */
|
||||
else if (!g_bg_building && (n_lines & 15) == 0)
|
||||
spinner_tick();
|
||||
|
||||
uint32_t p = idx_p;
|
||||
do {
|
||||
/* Пустая строка — разделитель параграфов.
|
||||
* В индекс добавляем не более одной пустой экранной строки подряд. */
|
||||
if (is_line_blank(p)) {
|
||||
@@ -708,13 +765,13 @@ void index_lines(void)
|
||||
|
||||
/* ---- fenced code-блок (граница или тело) ---- */
|
||||
if (is_fence_raw(p)) {
|
||||
in_block = !in_block;
|
||||
idx_in_block = !idx_in_block;
|
||||
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||
set_cur_flags(IF_NOWRAP);
|
||||
p = next_line(p);
|
||||
continue;
|
||||
}
|
||||
if (in_block) {
|
||||
if (idx_in_block) {
|
||||
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||
set_cur_flags(IF_NOWRAP | IF_CODE | IF_HSCROLL); /* код скроллится целым блоком */
|
||||
{
|
||||
@@ -926,43 +983,32 @@ void index_lines(void)
|
||||
p = next_line(q);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* emit_seg() флашит ячейки с отставанием на одну строку (см. её
|
||||
* комментарий) — последняя эмитированная строка файла так и остаётся
|
||||
* незафлашенной, её ячейки ещё в g_cells; коммитим здесь. */
|
||||
if (n_lines > 0) seg_commit_cells((uint16_t)(n_lines - 1));
|
||||
} while (0);
|
||||
idx_p = p;
|
||||
}
|
||||
|
||||
/* Лимит строк ловим ЗДЕСЬ, а не в emit_seg: главный цикл выходит по
|
||||
* `n_lines < max_lines` ДО того, как emit_seg будет вызван в переполненном
|
||||
* состоянии (для code-block — один emit_seg на строку, эта ветка не
|
||||
* достигается). Признак — остановились, а файл не кончился; g_abort и
|
||||
* исчерпание контента (TRUNC_CONTENT) уже обработаны/исключены. */
|
||||
if (!g_trunc_cause && !g_abort && p < file_size) {
|
||||
g_trunc_cause = TRUNC_LINES;
|
||||
g_trunc_line = (uint16_t)(max_lines - 1);
|
||||
}
|
||||
/* Выполняет до budget итераций индексации. Возврат: 1 — проход завершён
|
||||
* (индекс финализирован index_finish), 0 — есть ещё работа.
|
||||
* Конвертация UTF-8 держится впереди позиции чтения ЗДЕСЬ, до проверки
|
||||
* условия останова: пока исходник не сконвертирован до конца, idx_p не
|
||||
* может «догнать» file_size и оборвать проход преждевременно. */
|
||||
uint8_t index_step(uint8_t budget)
|
||||
{
|
||||
if (!idx_active) return 1;
|
||||
while (budget-- > 0) {
|
||||
/* UTF-8: держим конвертацию впереди позиции чтения (включая look-ahead
|
||||
* склейки/классификации ~ один абзац). file_size при этом = доступная
|
||||
* сконвертированная часть и растёт по ходу. */
|
||||
if (g_utf_building && (uint32_t)(idx_p + CONV_MARGIN) > file_size)
|
||||
utf_convert_more((uint32_t)(idx_p + CONV_MARGIN));
|
||||
|
||||
/* Файл был обрезан до 256 КБ при загрузке, а индексация дошла до конца
|
||||
* клампнутых данных без исчерпания контента/строк → ДОПИСЫВАЕМ строку
|
||||
* (место есть: n_lines < max_lines). Приоритет ниже content/lines —
|
||||
* те означают, что обрезали ещё раньше, их сообщение точнее. */
|
||||
if (!g_trunc_cause && !g_abort && g_file_clamped) {
|
||||
g_trunc_cause = TRUNC_FILE;
|
||||
g_trunc_line = n_lines;
|
||||
}
|
||||
|
||||
/* Обрыв по контенту/лимиту строк (не путать с пользовательским g_abort):
|
||||
* документ заканчиваем строкой-сообщением на g_trunc_line. Сообщение
|
||||
* рисуется вживую (IF_TRUNC_MSG) — место в контент-кэше ему не нужно, что
|
||||
* и снимает тупик «кэш полон, а сообщение тоже надо закэшировать». */
|
||||
if (g_trunc_cause && !g_abort) {
|
||||
cache_rec_t r;
|
||||
r.page = 0; r.off = 0; r.len = 0;
|
||||
r.flags = IF_TRUNC_MSG; r.reserved = g_trunc_cause;
|
||||
r.pad[0] = 0; r.pad[1] = 0;
|
||||
cache_dir_put(g_trunc_line, &r);
|
||||
n_lines = (uint16_t)(g_trunc_line + 1); /* документ кончается на строке-обрыве */
|
||||
if (!(idx_p < file_size && n_lines < max_lines && !g_abort && !g_trunc_cause)) {
|
||||
index_finish();
|
||||
return 1;
|
||||
}
|
||||
index_iter();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#define EM_NONE 0
|
||||
#define EM_BOLD 1
|
||||
|
||||
Reference in New Issue
Block a user