Elõre: A
saját include file Fel: Összetett
mintapélda Vissza: Saját
include file-ok
A menükezelõ rendszer
listája
/************************************************************
* File: menu.c *
* Tartalom: Menukezelo mintaprogram *
*************************************************************/
#include <stdio.h> /* Standard i/o csomag */
#include <string.h> /* Sztring- es memoriakezelo rutinok */
#include <stdlib.h> /* Altalanos celu standard fuggvenyek */
#include <ctype.h> /* Karakterkezelo makrok */
#include "myfunc.h" /* Sajat fuggvenyek prototipusai */
#include "mysymb.h" /* Szimbolumok (spec. billentyuk kodjai)*/
/* ======================================================== */
/* Tipusdeklaraciok */
typedef int intfunc(int);/* int-et visszaado, 1 int-et varo *
* fuggvenytipus */
typedef intfunc *fad; /* intfunc tipusu tarolasi egy- *
* segre mutato tipus */
typedef struct
{
char *text; /* A menupont azonosito szovege */
char key; /* A menupontot kivalaszto betu */
int helpindex; /* A menuponthoz rendelt help-kod */
fad function; /* A menuponthoz tartozo fv-re mutat */
int param; /* A '*function' fuggveny parametere */
} menuitem;
typedef struct
{
char *header; /* A menu fejlecszovegere mutat */
int x; /* A menudoboz bal felso sarkanak */
int y; /* x es y koordinatai, valamint */
int xs; /* a menudoboz x es */
int ys; /* y iranyu merete. */
int itemno; /* A menupontok szama */
menuitem *items; /* A menupontok listajara mutat. */
int hierarch; /* Ha 1, kozvetlenul a fomenu hivja */
int lastitem; /* Utoljara kivalasztott pont szama */
} menutype;
/* ======================================================== */
/* Tarolasi egysegek deklaracioi, definicioi: */
static char exitxt[~] = "eXit";
/* Majd sokszor kell ez a sztring. */
/* Kulso fuggvenyek deklaracioja */
extern intfunc data, r_data, w_data, statf,
regr, linf, barf,
save, load;
/* A a menukezelo fuggveny prototipus erteku deklaracioja */
intfunc menu; /* Elore hivatkozashoz */
intfunc dir, shell; /* Tovabbi fv-ek elore hivatkozshoz */
/* Az egyes menulistak (items_0 .. items_3) es a menuk: */
menuitem items_0[ ] =
{ /* text key hlp func. param. */
"Directory", 'D', 1, dir, 0,
"Os shell", 'O', 2, shell, 0,
"File", 'F', 3, menu, 3,/*a 3.sz. menu almenu lesz */
exitxt, 'X',-1, NULL, 0 /*-1-es parameter: exit */
}; /* Tombmeret: */
#define N0 sizeof(items_0)/sizeof(menuitem)
menuitem items_1[ ] =
{
"Default", 'D', 4, data, 7,
"Read data", 'R', 5, r_data,1,
"List data", 'L', 6, w_data,2,
"Statistics",'S', 7, statf, 3,
exitxt, 'X',-1, NULL, 0
};
#define N1 sizeof(items_1)/sizeof(menuitem)
menuitem items_2[ ] =
{
"Regression",'R', 8, regr, 4,
"Plot", 'P', 9, linf, 5,
"Bar", 'B',10, barf, 6,
exitxt, 'X',-1, NULL, 0
};
#define N2 sizeof(items_2)/sizeof(menuitem)
menuitem items_3[ ] =
{
"Save", 'S',11, savef, 0,
"Load", 'L',12, loadf, 0,
exitxt, 'X',-1, NULL, 0
};
#define N3 sizeof(items_3)/sizeof(menuitem)
/* A teljes menurendszer leirasa: */
menutype menus[ ] =
{/* head. x y xs ys itemno items hier. last */
"", 9, 2, 13, N0+3, N0, items_0, 1, 0,
"", 35, 2, 14, N1+3, N1, items_1, 1, 0,
"", 61, 2, 14, N2+3, N2, items_2, 1, 0,
"Files",11, 6, 8, N3+3, N3, items_3, 0, 1
};
/*
Mivel a fõmenünek semmi más funkciója
nincs, mint a menu függvénynek átadni a vezérlést
a megfelelõ menüindexszel, komolyabb adatstruktúrákat
nem definiáltunk a számára. Csak az alábbiakra
van szükség a fõmenühöz:
*/
static char main_header[ ] = /* A fomenu fejlecszovege */
" Highly Portable Menu System ";
static char options[ ]=/*Az egyes menuk kivalaszto gombjai */
"FDP"; /*Sorrendjuk ugyanaz, mint az alab- */
/*bi sztring-tomb elemeinek sorrendje*/
/*
Az options sztring hossza adja meg, hogy a menus
tömb hányadik eleméig tekintjük a menüket
a fõmenü részeinek.
*/
static char *headers[ ]= { "File", /* A fomenube felvett */
"Data", /* menuk fejlec szove- */
"Plot" /* gei. */
};
static int mainselect = 0; /* Az utoljara kiv.fomenu elem */
static char buffer[81]; /* Ide generaljuk a fomenut */
static int xp,yp,j; /* Segedvaltozok a rutinokhoz */
static char inpbuff[256]; /* Altalanos input buffer */
/*
A magyarázatok és deklarációk után
következzenek maguk a függvények! A menükezelõ
rendszert úgy hoztuk létre, hogy programunk main-je
csak ilyen rövid legyen:
*/
/************************************************************/
void main(void) /* Ez tetszoleges program 'main'-je lehet */
/************************************************************/
{
displ_ini(); /* A kepernyokezelo rendszer inicializalasa */
main_frame();/* Keretrajzolas a fomenuhoz: mint az IDE */
main_menu(0);/* Fomenu. Addig fut, amig ESC-pel
ki nem szallnak belole */
displ_end(); /* A kepernyo alapallapotanak
helyreallitasa */
exit(0); /* OK hibakod visszadasa az operacios
rendszernek */
}
/* Most tekintsük magát a menükezelõ
rutincsomagot! */
/************************************************************/
int menu(int index)/*Az aktualisan hasznalando menu indexe */
/*
Funkció:
A függvény a menus[index]-ben adott menüt
megjeleníti a képernyõn. Az egyes menüpontokat
a menüleírás szerinti dobozban jeleníti meg.
A menus[index].lastitem indexû menüpont kiemelve látszik
a képen. A kiemelt menüpontot a
és
kurzorvezérlõkkel változtathatjuk. Ha leütjük
az Enter billentyût, akkor a kiemelt szinû menüpont függvényét
hivjuk meg, ha pedig valamelyik menüponthoz rendelt nagybetût
ütjük le a billentyûzeten, akkor az illetõ menüpont
függvénye lesz aktivizálva a menus[index].items[selected].param
parameterrel, ahol index a kiválasztott menüpont indexe.
Amint a meghívott függvény visszaadja a vezérlést,
a menu szubrutin regenerálja az aktuális menülistát
a keretezett dobozban. Ha menus[index].hierarch == 1 akkor a menu
függvény visszatérési értéke
-
-
-
RIGHT ha a
kurzorvezérlõ gombot nyomták meg,
-
-
-
LEFT ha a
kurzorvezérlõ gombot nyomták meg.
Minden egyéb esetben a visszatérési érték
0, tehát amikor
-
-
-
az ESC gombot nyomták meg (kilépés a menu
függvénybõl),
-
-
-
olyan menüpontot választottak ki, amelynek a helpindex-e
-1
*************************************************************/
{
int i, /* A menupontok szamat tesszuk bele */
l, /* for-ciklushoz ciklusvaltozo */
exit, /* Kilepest jelzo flag */
par, /* A kivalasztott fv. parametere */
cmd; /* A vezerlo-karakternek */
/* .......... E L O K E S Z I T E S E K ............... */
j = menus[index].lastitem;/* j-ben az aktualis index */
i = menus[index].itemno;
if (!i) return 0; /* Nulla meretu menuvel nem torodunk */
menu_regen(index,1);/* A menut kiiratjuk a kepernyore */
exit = FALSE; /* A kilepest jelzo flag kezdoerteke */
/* .............. F O C I K L U S ................... */
while (! exit) /*Addig tart,amig exit igaz nem lesz */
{
cmd = 0; /* Kezdetben ures parancs */
while (!(cmd == SELECT || cmd == CR))
{
cmd = getkey(); /* VT100-on ketfele ENTER van, */
switch(cmd) /* ezert van CR is es SELECT is. */
{
case BEGIN:
o_gotoxy(xp,yp+j); /* HOME-ot nyomott */
printf("%s",menus[index].items[j].text);
j = 0;
o_gotoxy(xp,yp);
highlight(EMPHAS,menus[index].items[j].text);
break;
case END:
o_gotoxy(xp,yp+j); /* END-et nyomott */
printf("%s",menus[index].items[j].text);
j = i-1;
o_gotoxy(xp,yp+j);
highlight(EMPHAS,menus[index].items[j].text);
break;
case UP: /* 'fel' nyil */
{
o_gotoxy(xp,yp+j);
printf("%s",menus[index].items[j].text);
if (j > 0) j--; else j = i - 1;
o_gotoxy(xp,yp+j);
highlight(EMPHAS,menus[index].items[j].text);
}
break;
case DOWN: /* 'le' nyil */
{
o_gotoxy(xp,yp+j);
printf("%s",menus[index].items[j].text);
if (j < i-1) j++; else j = 0;
o_gotoxy(xp,yp+j);
highlight(EMPHAS,menus[index].items[j].text);
}
break;
case HELP: /* F1-et nyomtak */
menus[index].lastitem = j;
menu_help(menus[index].items[j].helpindex);
if (menus[index].items[j].helpindex >= 0 &&
menus[index].y + menus[index].ys > 11)
menu_regen(index,0);
break;
case ESC: /* ESC-et nyomtak */
exit = 1;
cmd = SELECT;
break;
case LEFT:
case RIGHT:
/* Ha 'main_menu' hivta 'menu'-t, akkor a
'jobbra', 'balra' nyilak eseten a menut to-
roljuk, es a nyil-gomb kodjat visszaadjuk.
Igy a fomenu a roll-in menut felvaltja egy
masikkal: */
if (menus[index].hierarch == 1)
{
menu_remove(index);
return cmd;
}
default:
/* Kilepunk, ha dedikalt gombot nyomtak */
if (cmd < 128)
{
cmd = toupper(cmd);
for(l = 0; l < i; l++)
{
if (menus[index].items[l].key == cmd)
{
o_gotoxy(xp,yp+j);
printf("%s",menus[index].items[j].text);
cmd = SELECT;
j = l;
break;
}
}
}
break;
} /* ............... end switch .................. */
} /* ................. end while .................... */
if (! exit)
{
exit = (menus[index].items[j].helpindex == -1);
}
/*
Ezen a ponton már eldõlt, hogy ki akarunk-e lépni.
Ha nem, akkor viszont tudjuk, hogy melyik menüpont függvényét
kell aktivizálni:
*/
if (! exit)
{ /* Az 'eXit' pontnak mindig -1 helpindexe legyen! */
/* .... A kivalasztott fuggveny aktivizalasa:.... */
/* (j indexeli a kivalasztott fuggvenyt) */
o_gotoxy(xp,yp+j);
highlight(EMPHAS,menus[index].items[j].text);
menus[index].lastitem = j;
par = menus[index].items[j].param
(*menus[index].items[j].function)(par);
menu_regen(index,0); /* A menu-box regeneralasa */
}
else
{
menu_remove(index);
}
}
return 0;
}
/************************************************************/
void menu_regen(int index, /* A regeneralando menu indexe */
int rem) /* TRUE: torolni kell a dobozt */
/*
Funkció:
A menus[index] menü regenerálása (újra
rajzolja a dobozt, kiírja a menülistát és kiemelõ
szinnel nyomtatja az utoljára kiválasztott menüpontot.)
Ha rem == 1 akkor a menü által elfoglalt képernyõterületet
törli a menülista kiírása elõtt, ha rem
== 0, akkor nem töröl.
*************************************************************/
{
int i,k,l,m,n,xx,yy;
int x1,x2;
xp = menus[index].x; /* Pozicio, meret elovetele */
yp = menus[index].y;
i = menus[index].itemno;
xx = menus[index].xs;
yy = menus[index].ys;
/* Dobozrajzolas */
box_draw(menus[index].header,xp,yp,xx,yy,rem);
xp += 2;
yp += 2;
for (k = 0; k < i; k++) /* A menulista megjelenitese */
{
o_gotoxy(xp,yp+k);
if (k == menus[index].lastitem)
{
highlight(EMPHAS,menus[index].items[k].text);
j = k;
}
else
printf("%s",menus[index].items[k].text);
}
}
/************************************************************/
void menu_remove(int index) /* A torlendo menu indexe */
/*
Funkció:
A menus[index] menü törlése a képernyõrõl
*************************************************************/
{
int xx,yy,x1,y1;
x1 = menus[index].x;
y1 = menus[index].y;
xx = menus[index].xs;
yy = menus[index].ys;
box_delete(x1,y1,xx,yy);
}
/************************************************************/
void box_draw(char* header, /* ->a doboz fejlec-szevege */
int xp, int yp,/* a doboz pozicioja, */
int xs, int ys,/* merete */
int rem) /* 1, ha torles kell, egyebkent 0 */
/*
Funkció:
Egy xs, ys méretû dobozt rajzol az
xp, yp pozicióba. A keret felsõ részének
közepére a header fejlécet írja ki.
Ha rem == 1, akkor a doboz rajzolása elõtt törli
a doboz által elfoglalandó területet.
*************************************************************/
{
int l,n,xx,yy;
int x1,x2;
l = strlen(header); /* A fejlec hossza */
xx = xs-2; /* Egyeb adatok elokeszitese */
x1 = (xx - l)/2;
x2 = xx - (x1 + l);
yy = ys-2;
if (rem) box_delete(xp,yp,xs,ys);
o_gotoxy(xp,yp); /* A legfelso sor a fejleccel */
printf("%c",UPLEFT);
for (n = 0; n < x1; n++) printf("%c",HORIZ);
highlight(REVERSE BRIGHT,header);
for (n = 0; n < x2; n++) printf("%c",HORIZ);
printf("%c",UPRIGHT);
yp++;
for (n = 0; n < yy; n++) /* Maga a doboz */
{
o_gotoxy(xp,yp+n);
printf("%c",VERT);
o_gotoxy(xp+1+xx,yp+n);
printf("%c",VERT);
}
o_gotoxy(xp,yp+yy); /* A doboz legalso sora */
printf("%c",DOWNLEFT);
for (n = 0; n < xx; n++) printf("%c",HORIZ);
printf("%c",DOWNRIGHT);
}
/************************************************************/
void box_delete(int xp, int yp, /* Egy dobozt torol */
int xs, int ys) /* Pozicio, meret */
/*
Funkció:
Egy xs, ys méretû dobozt töröl
az xp, yp pozicióról.
*************************************************************/
{
int n, m;
for (n = ys-1; n >= 0; n--)
{
o_gotoxy(xp,yp+n);
for (m = 0; m < xs; m++) putc(' ');
}
}
/************************************************************/
void menu_help(int index) /* A menupont help-indexe */
/*
Funkció:
Az index által meghatározott help-szöveget
kikeresi egy help-file-ból, és kiírja a képernyõre.
A kiíráshoz egy 7 soros ablakot nyit, a szöveget 7 soronként
írja ki. Ha van még kiirandó szöveg, akkor a
More ... üzenet után egy billentyûleütésre
vár, ha nincs, akkor a Press any key ... üzenet után
törli a képernyõrõl a help-dobozt, és
visszatér. A help-file formátuma a következõ:
index_i n_i
sor_1_i
sor_2_i
...
sor_n_i
index_j n_j
...
ahol index_i az i-edik help-index, n_i az ehhez az
indexhez tertozó help-szöveg sorainak a száma, valamint
sor_1_i, ... sor_n_i a help-szöveg egyes sorai.
Formátum hiba, vagy file vége esetén szintén
hibajelzés történik.
*************************************************************/
{
static char hunex[] = "Unexpected end of the help-file!!!";
#define YP 24
FILE *fp;
int i,j,k,err;
if (index < 0) return; /* Negativra visszater */
box_draw(" HELP ",2,11,76,9,1);/* Help-box rajzolasa */
fp = fopen(helpdat,"r"); /* Help-file megnyitasa */
if (fp == NULL) /* Ha nem letezik a file, hibajelzes */
{
o_gotoxy((80-(21+strlen(helpdat)))/2-1,16);
printf("Help-file %s not found!",helpdat);
goto helpend1;
}
i = -1;
while (i != index) /* Help-index keresese a file-ban */
{
err = fscanf(fp,"%d%d",&i,&j);
if (err == EOF) /* Nem talaljuk: hibajelzes */
{
o_gotoxy(19,16);
printf("No help is available for this menu item!");
goto helpend0;
}
if (err != 2)
{
o_gotoxy((79-(31+strlen(helpdat)))/2,16);
printf("Format error in the %s help-file!",helpdat);
goto helpend0;
}
if (i != index)/* Ha meg nem talalja, tovabb olvas. */
{
for (; j >= 0; j--)
{
if (NULL == fgets(inpbuff,74,fp))
{
o_gotoxy((79-strlen(hunex))/2,16);
printf(hunex);
goto helpend0; /* A 'goto'-t legfeljebb igy */
} /* hasznaljuk! */
}
}
}
for (k = i = 0; i < j; i++)
{
if (NULL == fgets(inpbuff,74,fp))
{
o_gotoxy((79-strlen(hunex))/2,16);
printf(hunex);
goto helpend0;
}
o_gotoxy(4,12+k);
printf(inpbuff);
k++;
if (k == 7)/*Megvan a helpszoveg. 7-esevel kiirjuk: */
{
o_gotoxy(66,YP);
highlight(BRIGHT,"More ...");
bell();
err = getkey();
o_gotoxy(66,YP);
printf(" ");
if (err == ESC)/* ESC-re kiszallunk */
{
fclose(fp);
box_delete(2,11,76,9);
return;
}
box_draw(" HELP ",2,11,76,9,1);
k = 0;
}
}
helpend0: /* Minden befejezeskor ezeket a muveleteket */
fclose(fp); /* kell elvegezni, tehat takarekos meg- */
helpend1: /* oldas a 'goto' hasznalat. Csinjan ban- */
press_key();/* junk az ilyennel, hogy olvashato marad- */
box_delete(2,11,76,9); /* jon a programunk! */
}
/************************************************************/
void main_frame(void)
/*
Funkció:
Keretet rajzol a fõmenünek. Ha valamelyik függvény
törli az egész képernyõt, akkor main_frame
meghívásával helyreállíthatja azt.
*************************************************************/
{
erase();
box_draw(main_header,0,0,80,23,0);/* Main box fejleccel */
main_menu(1); /* Fomenu statusz-sora */
}
/************************************************************/
void main_menu(int stl)/*Ha 1, akkor a statusz-sort kiirja */
/*
Funkció:
A menükezelõ rendszer fõ rutinja, ezt kell a
main-bõl meghívni. A menus tömbbõl
annyi menüt kezel közvetlenül, amennyi az options
sztring hossza. A menü-opciókat a képernyõ második,
kivilágított sorában jeleníti meg. Egy menüpont
a szokásos módon választható (kurzorral kiemelés,
majd Enter, vagy a kezdõ betû leütése). Ha egy
almenü él (azaz látszik a képernyõn),
akkor a
,
illetve
nyilakkal a szomszédos menüre válthatunk.
*************************************************************/
{
int i,j,k,l,
posinc,
hno,xp,
cmd,flag;
/* 'buffer'-ben lesz az inverzen kiirando statusz-sor */
hno = sizeof(headers)/sizeof(char*);
posinc = 78/hno;
xp = posinc/2;
if (stl)
{
for (i = 0; i < 78; buffer[i++] = ' ')
;
for (j = 0; j < hno; j++)
{
l = strlen(headers[j]);
for(k = 0; k < l; k++)
buffer[xp+j*posinc+k] = *(headers[j]+k);
}
buffer[78] = '\0';
o_gotoxy(1,1);
highlight(REVERSE,buffer);
}
/* A kivalasztott menut normal modon jelenitjuk meg: */
i = mainselect;
xp++;
if (stl)
{
o_gotoxy(xp+i*posinc,1);
printf(headers[i]);
return;
}
/* A fo parancs-ciklus. Csak ESC-re lephetunk ki belole. */
dontquit: /* Ide ugrunk, ha megse lepunk ki. */
flag = cmd = 0;
while (cmd != ESC)
{ if (! flag) cmd = getkey();
flag = 0;
switch (cmd) /* Nincs elo almenu. Kurzorvezerlok */
{ /* feldogozasa, status-sor modositasa */
case RIGHT:
o_gotoxy(xp+i*posinc,1);
highlight(REVERSE,headers[i]);
if (i < hno-1) i++; else i = 0;
o_gotoxy(xp+i*posinc,1);
printf(headers[i]);
break;
case LEFT:
o_gotoxy(xp+i*posinc,1);
highlight(REVERSE,headers[i]);
if (i) i--; else i = hno-1;
o_gotoxy(xp+i*posinc,1);
printf(headers[i]);
break;
case SELECT:
crselect:
/* Kivalasztottak egy almenut. Megje- */
/* gyezzuk indexet 'mainselect'-ben */
mainselect = i;
flag = menu(i);/* A 'menu' rutin behivasa */
switch (flag) /* Mi a visszateresi ertek? */
{
case RIGHT: /* Stat.sor modositas ... */
o_gotoxy(xp+i*posinc,1);
highlight(REVERSE,headers[i]);
if (i < hno-1) i++; else i = 0;
o_gotoxy(xp+i*posinc,1);
printf(headers[i]);
break;
case LEFT:
o_gotoxy(xp+i*posinc,1);
highlight(REVERSE,headers[i]);
if (i) i--; else i = hno-1;
o_gotoxy(xp+i*posinc,1);
printf(headers[i]);
break;
}
l = strlen(headers[i]);
o_gotoxy(xp+i*posinc+l,1);
break;
default:
if (cmd < 128) /*Kezdobetuvel valasztottak */
{
cmd = toupper(cmd);
for (l = 0; l < hno; l++)
if (cmd == options[l])
{
o_gotoxy(xp+i*posinc,1);
highlight(REVERSE,headers[i]);
i = mainselect = l;
o_gotoxy(xp+i*posinc,1);
printf(headers[i]);
/* Ugy teszunk, mintha 'nyil+Enter'-rel
valasztottak volna. */
cmd = SELECT;
goto crselect;
}
}
break;
}
}
/* Az ESC-pel valo kilepes szandekat megerosittetjuk: */
box_draw("",28,5,24,3,0);
o_gotoxy(30,6);
highlight(BRIGHT,"Are you sure? (y/n) ");
cmd = yesno();
box_delete(28,5,24,3);
o_gotoxy(1,1);
if (!cmd) goto dontquit; /*Nem lep ki, vissza az elejere */
erase();
}
*************************************************************/
/*
Két gyakori funkció portábilis megvalósítását
találjuk itt. Ezek az aktív könyvtár tartalmának
kiiratása a képernyõre, illetve az operációs
rendszer parancs-értelmezõ burkának (command shell)
az aktivizálása. Mindkettõt a system függvény
segítségével oldjuk meg. A system argumentuma
egy operációs rendszernek szóló parancsot tartalmazó
sztring. Ezek a mi esetünkben egy-egy #define makróként
lettek megadva, így azok operációs rendszertõl
függõ feltételes fordítással megfelelõen
beállíthatók. Tahát a system függvénynek
(process.h) átadandó operációs endszer
parancsok:
*/
#ifdef __MSDOS__
#define DIRSTR "dir /w/p"
/* A burok (shell) 'dir' parancsa */
#define SHELL "COMMAND.COM"
/* Maga az operacios r. burok (shell) */
#endif
/*
A fenti sztringeket csak a DOS-ban adhatjuk át a system-nek,
ezért használtuk az #ifdef __MSDOS__ fordításvezérlõ
direktívát. UNIX-ban a megfelelõ sztringek értéke
rendre "ls -C |more", illetve "sh" lenne.
*/
/************************************************************/
int dir(int d) /* Az aktiv konyvtar tartalmat nezzuk meg */
/* d: Dummy parameter */
/************************************************************/
{
displ_end();
system(DIRSTR); /* Az op. rendszer DIR parancsat kerjuk.
Ennel lehetne hatekonyabb megoldast
is talalni, de ez igy portabilis. */
displ_ini();
press_key();
erase();
main_frame();
return d;
}
/************************************************************/
int shell(int d)/* A parancsertelmezo burok hivasa */
/* d: Dummy parameter */
/************************************************************/
{
displ_end();
printf("\nType exit to return!\n\n");
system(SHELL);
displ_ini();
erase();
main_frame();
return(x);
}
Elõre: A
saját include file Fel: Összetett
mintapélda Vissza: Saját
include file-ok