#help_index "Cmd Line (Typically)"

I64 DEPtrCompare(CDocEntry **e1, CDocEntry **e2)
{
        return StrCompare((*e1)->tag, (*e2)->tag);
}

public I64 Sort(U8 *_in_name, U8 *_out_name=NULL, I64 entry_lines=1, Bool unique=FALSE)
{//Sort lines of a text file. Removes blank lines.
        U8                      *in_name, *out_name, *st;
        CDoc            *doc;
        CDocEntry       *doc_e, *doc_e1, **a;
        I64                      i, j, count = 0, res;

        if (!_in_name)
                return 0;
        in_name = ExtDefault(_in_name, ".DD");
        if (_out_name)
                out_name = ExtDefault(_out_name, ".DD");
        else
                out_name = StrNew(in_name);

        doc = DocRead(in_name, DOCF_PLAIN_TEXT_TABS | DOCF_NO_CURSOR);
        doc_e = doc->head.next;
        while (doc_e != doc)
        {
                if (doc_e->type_u8 == DOCT_TEXT)
                        count++;
                doc_e = doc_e->next;
        }
        a = MAlloc(count * sizeof(CDocEntry *));
        doc_e = doc->head.next;
        i = 0;
        while (doc_e != doc)
        {
                doc_e1 = doc_e->next;
                if (doc_e->type_u8 == DOCT_TEXT)
                {
                        QueueRemove(doc_e);
                        a[i++] = doc_e;
                }
                else
                        DocEntryDel(doc, doc_e);
                doc_e = doc_e1;
        }
        QuickSort(a, count / entry_lines, entry_lines * sizeof(CDocEntry *), &DEPtrCompare);

        res = 0;
        st = NULL;
        for (i = 0; i < count;)
        {
                if (!unique || !st || StrCompare(a[i]->tag, st))
                {
                        st = a[i]->tag;
                        for (j = 0; j < entry_lines && i < count; j++, i++)
                        {
                                QueueInsert(a[i], doc->head.last);
                                doc->cur_entry  = &doc->head;
                                doc->cur_col    = 0;
                                DocPrint(doc,"\n");
                        }
                        res++;
                }
                else
                        for (j = 0; j < entry_lines && i < count; j++, i++)
                        {
                                QueueInsert(a[i], doc->head.last);
                                DocEntryDel(doc, a[i]);
                        }
        }
        StrCopy(doc->filename.name, out_name);
        DocWrite(doc);

        Free(a);
        DocDel(doc);
        Free(in_name);
        Free(out_name);

        return res; //Num Entries
}

I64 DocWordsFile(CDoc *doc_out=NULL, U8 *filename, U32 *char_bmp)
{
        U8                      *ptr, *ptr2;
        I64                      res = 0, ch;
        CDoc            *doc_in = DocRead(filename);
        CDocEntry       *doc_e = doc_in->head.next;

        while (doc_e != doc_in)
        {
                if (doc_e->de_flags & DOCEF_TAG)
                {
                        ptr = doc_e->tag;
                        while (*ptr)
                        {
                                while (*ptr && !Bt(char_bmp, *ptr))
                                        ptr++;

                                ptr2 = ptr;
                                while (*ptr && Bt(char_bmp, *ptr))
                                        ptr++;

                                ch = *ptr;
                                *ptr = 0;
                                if (*ptr2)
                                {
                                        DocPrint(doc_out, "%s\n", ptr2);
                                        res++;
                                }
                                *ptr = ch;
                        }
                }
                doc_e = doc_e->next;
        }
        DocDel(doc_in);

        return res;
}
public I64 Words(U8 *files_find_mask="*", U32 *char_bmp=char_bmp_alpha, U8 *fu_flags=NULL)
{//Break file into list of not-unique words.
        I64                      fuf_flags = 0, res = 0;
        CDoc            *doc_out = DocNew;
        CDirEntry       *tmpde, *tmpde1;

        FlagsScan(&fuf_flags, Define("ST_FILE_UTIL_FLAGS"), "+r+f+F+T");
        FlagsScan(&fuf_flags, Define("ST_FILE_UTIL_FLAGS"), fu_flags);
        tmpde = tmpde1 = FilesFind(files_find_mask, fuf_flags);

        while (tmpde)
        {
                res += DocWordsFile(doc_out, tmpde->full_name, char_bmp);
                tmpde = tmpde->next;
        }
        DirTreeDel(tmpde1);
        DocInsDoc(NULL, doc_out);
        DocDel(doc_out);

        return res;
}

I64 LongLinesFile(U8 *filename, I64 cols)
{
        I64                      res = 0;
        CDoc            *doc = DocRead(filename);
        CDocEntry       *doc_e = doc->head.next;

        while (doc_e != doc)
        {
                if (doc_e->type_u8 == DOCT_NEW_LINE && doc_e->x >= cols + 1)
                        res++;
                doc_e = doc_e->next;
        }
        DocDel(doc);
        if (res)
        {
                "%04d ", res;
                PutFileLink(filename);
                '\n';
        }

        return res;
}
public I64 LongLines(U8 *files_find_mask="*", I64 cols=80, U8 *fu_flags=NULL)
{//Report files with lines of too many cols.
        I64                      res = 0, fuf_flags = 0;
        CDirEntry       *tmpde, *tmpde1;

        FlagsScan(&fuf_flags, Define("ST_FILE_UTIL_FLAGS"), "+r+f+F+S");
        FlagsScan(&fuf_flags, Define("ST_FILE_UTIL_FLAGS"), fu_flags);
        tmpde = tmpde1 = FilesFind(files_find_mask, fuf_flags);

        while (tmpde)
        {
                if (LongLinesFile(tmpde->full_name, cols))
                        res++;
                tmpde = tmpde->next;
        }
        DirTreeDel(tmpde1);

        return res;
}

U0 SUFile(U8 *filename,I64 suf_flags,F64 indent_scale_factor)
{//String utility on a single file
//See SU Flags
        U8                      *dst;
        Bool            chged = FALSE;
        I64                     reduced = 0;
        CDoc            *doc = DocRead(filename, DOCF_PLAIN_TEXT_TABS | DOCF_NO_CURSOR);
        CDocEntry       *doc_e = doc->head.next;

        while (doc_e != doc)
        {
                if (doc_e->type_u8 == DOCT_TEXT)
                {
                        dst = MStrUtil(doc_e->tag, suf_flags, indent_scale_factor);
                        if (StrCompare(dst, doc_e->tag))
                        {
                                reduced += StrLen(doc_e->tag) - StrLen(dst);
                                chged = TRUE;
                                Free(doc_e->tag);
                                doc_e->tag = dst;
                        }
                        else
                                Free(dst);
                }
                doc_e = doc_e->next;
        }
        if (chged)
        {
                "Reduced %s by %d chars\n", filename, reduced;
                DocWrite(doc);
        }
        DocDel(doc);
}
public U0 SU(U8 *files_find_mask, I64 suf_flags, U8 *fu_flags=NULL, F64 indent_scale_factor=0)
{//Apply StrUtil() on files.
//You can convert spaces to tabs, for example,
        //or removing trailing spaces on lines.
        //See SUF Flags.
        I64                      fuf_flags = 0;
        CDirEntry       *tmpde, *tmpde1;

        FlagsScan(&fuf_flags, Define("ST_FILE_UTIL_FLAGS"), "+f+F+T");
        FlagsScan(&fuf_flags, Define("ST_FILE_UTIL_FLAGS"), fu_flags);
        tmpde = tmpde1 = FilesFind(files_find_mask, fuf_flags);

        while (tmpde)
        {
                SUFile(tmpde->full_name, suf_flags, indent_scale_factor);
                tmpde = tmpde->next;
        }
        DirTreeDel(tmpde1);
}

public U0 S2T(U8 *files_find_mask, U8 *fu_flags=NULL)
{//Spaces to tabs.
//Use "Hard Space" (SHIFT-SPACE) for spaces
        //in string consts in your code.
        SU(files_find_mask, SUF_S2T | SUF_REM_TRAILING, fu_flags);
}