home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 454.lha / fsi / fsi.c < prev    next >
C/C++ Source or Header  |  1990-12-08  |  18KB  |  715 lines

  1. /*
  2.  
  3.     fsi - a comprehensive File System Information utility for the Amiga
  4.  
  5.     Usage: fsi [-x] [device|volume|assign|file|directory name] [...]
  6.            Include -x if you want extended device information
  7.  
  8. */
  9.  
  10.  
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include <dos.h>
  14. #include <libraries/dos.h>
  15. #include <libraries/dosextens.h>
  16. #include <libraries/filehandler.h>
  17. #include <exec/tasks.h>
  18. #include <exec/nodes.h>
  19. #include <exec/ports.h>
  20. #include <exec/memory.h>
  21. #include <proto/exec.h>
  22. #include <proto/dos.h>
  23.  
  24.  
  25. #define toAPTR(b) (void *)((LONG)(b)<<2)
  26. #define toBPTR(a) (BPTR)((LONG)(a)>>2)
  27. #define ID_OFS_DISK 0x444F5300
  28. #define ID_FFS_DISK 0x444F5301
  29.  
  30.  
  31. byte extended = 0;  /* Captain! I see a flag in the distance! */
  32.  
  33.  
  34. char *c;
  35. char name[FMSIZE+1];
  36. APTR old_pr_WindowPtr;
  37.  
  38.  
  39. BPTR lock                 = NULL;
  40. struct Process *process   = NULL;
  41. struct InfoData *info     = NULL;
  42. struct FileInfoBlock *fib = NULL;
  43.  
  44.  
  45. void clean_exit(int xcode)
  46. {
  47.   if (lock) UnLock(lock);
  48.   if (info) FreeMem(info,sizeof(struct InfoData));
  49.   if (fib) FreeMem(fib,sizeof(struct FileInfoBlock));
  50.   if (process)
  51.     {
  52.       Forbid();
  53.       process->pr_WindowPtr = old_pr_WindowPtr;
  54.       Permit();
  55.     }
  56.   printf("\n");
  57.   exit(xcode);
  58. }
  59.  
  60.  
  61. int strsam(char *str1, char *str2)
  62. {
  63.   while( (*str1) && (*str2) )
  64.     {
  65.       if ( toupper(*str1) != toupper(*str2) ) return(0);
  66.       str1++;
  67.       str2++;
  68.     }
  69.   if ( (!*str1) && (!*str2) ) return(1);
  70.   return(0);
  71. }
  72.  
  73.  
  74. int bstrsam(char *str1, BSTR bstr)
  75. {
  76.   register char *str2 = toAPTR(bstr);
  77.   if (strlen(str1) != (int)*str2) return(0);
  78.   str2++;
  79.  
  80.   while(*str1)
  81.     {
  82.       if ( toupper(*str1) != toupper(*str2) ) return(0);
  83.       str1++;
  84.       str2++;
  85.     }
  86.   return(1);
  87. }
  88.  
  89.  
  90. void bstrcpy(char *str, BSTR bstr)
  91. {
  92.   register char *c = toAPTR(bstr);
  93.   register int len = (int)*c;
  94.   stccpy(str, ++c, ++len);
  95. }
  96.  
  97.  
  98. void get_bcplname(char *str, BSTR bstr)
  99. {
  100.   register char *c = toAPTR(bstr);
  101.   if ( (!c) || (!*c) )
  102.     {
  103.       strcpy(str,"›3munknown›0m");
  104.     }
  105.   else
  106.     {
  107.       bstrcpy(str,bstr);
  108.     }
  109. }
  110.  
  111.  
  112. struct DeviceList *DevList(void)
  113. {
  114.   register struct DosInfo *dos;
  115.   register struct RootNode *root;
  116.   root = (struct RootNode *)DOSBase->dl_Root;
  117.   dos = toAPTR(root->rn_Info);
  118.   return(toAPTR(dos->di_DevInfo));
  119. }
  120.  
  121.  
  122. /*
  123. **  this function will search for a node which has the specified task 
  124. **  pointer and type -- although one trackdisk.device task can handle
  125. **  two or more drives, each FileSystem handler can only handle one drive.
  126. **  therefore, each drive in your system (DF0:,DF1:,DF2:,DF3:,etc.) gets
  127. **  its own FileSystem task.  to put it another way, each DLT_DEVICE has a
  128. **  unique dn_Task field.  When a volume is mounted in a drive, the
  129. **  volume's DLT_VOLUME node contains a pointer in the dn_Task field which
  130. **  identifies the handler task for the drive that the volume is mounted
  131. **  on.  you can search the device list for that drive/device if you wish.
  132. **  likewise, to find what volume is mounted on a particular device,
  133. **  search the list for a DLT_VOLUME that has the same dn_Task pointer.
  134. **  this function is meant to be used in both situations.  note that if a
  135. **  DLT_VOLUME node has a NULL dn_Task field that means that the volume is
  136. **  NOT currently mounted in any drive.  likewise, if you have a device
  137. **  node but fail to find a volume node with the same dn_Task, there is no
  138. **  volume currently mounted in that device.
  139. */
  140. struct DeviceNode *find_obj(struct MsgPort *task, ULONG type)
  141. {
  142.   register struct DeviceNode *dn;
  143.   if (!task) return(NULL);
  144.   Forbid();
  145.   for(dn = (struct DeviceNode *)DevList(); dn; dn = toAPTR(dn->dn_Next))
  146.     {
  147.       if ( (dn->dn_Type == type) && (dn->dn_Task == task) ) break;
  148.     }
  149.   Permit();
  150.   return(dn);
  151. }
  152.  
  153.  
  154. /*
  155. **  this function will search the device list for a node with the given name
  156. */
  157. struct DeviceNode *lookup_name(char *name)
  158. {
  159.   register struct DeviceNode *dn;
  160.   register char *colon;
  161.  
  162.   if ( (!name) || (!*name) ) return(NULL);
  163.  
  164.   for(colon = name; *colon; colon++);
  165.   colon--;
  166.   if (*colon == ':') *colon = '\0'; /* it should... */
  167.  
  168.   Forbid();
  169.  
  170.   for(dn = (struct DeviceNode *)DevList(); dn; dn = toAPTR(dn->dn_Next))
  171.     {
  172.       if (bstrsam(name,dn->dn_Name)) break;
  173.     }
  174.  
  175.   Permit();
  176.  
  177.   if (*colon == '\0') *colon = ':';
  178.  
  179.   return(dn);
  180. }
  181.  
  182.  
  183. void show_datestamp(struct DateStamp *ds)
  184. {
  185.   char time_date[26];
  186.   ULONG secs;
  187.  
  188. /* calculate time & date in seconds */
  189.   secs=(
  190.         (ds->ds_Days*86400L)            /* days since 1978 * secs per day  */
  191.         +(ds->ds_Minute*60)             /* minutes since midnite * 60 secs */
  192.         +(ds->ds_Tick/TICKS_PER_SECOND) /* seconds since minute            */
  193.         +252460800L /* number of seconds between 1970 &1978 (2 leap years) */
  194.         +21600L     /* correction factor of six hours (for DST)...         */
  195.                     /* obviously this will not work properly everywhere    */
  196.                     /* (can you say K-L-U-D-G-E ??)                        */
  197.        );
  198.   strcpy(time_date,(char *)ctime(&secs));
  199.   time_date[24]='\0';
  200.  
  201.   printf("Last modified: %s\n",time_date);
  202. }
  203.  
  204.  
  205. void show_protection(ULONG p)
  206. {
  207.   char protection[9];
  208.  
  209. /* build protection string */
  210.   protection[8]='\0';
  211.   protection[7]=(p&1L) ?'-':'d';
  212.   protection[6]=(p&2L) ?'-':'e';
  213.   protection[5]=(p&4L) ?'-':'w';
  214.   protection[4]=(p&8L) ?'-':'r';
  215.   protection[3]=(p&16L)?'a':'-';
  216.   protection[2]=(p&32L)?'p':'-';
  217.   protection[1]=(p&64L)?'s':'-';
  218.   protection[0]='-';
  219.  
  220.   printf("Protection bits: %s\n",protection);
  221. }
  222.  
  223.  
  224. void extended_devinfo(struct DeviceNode *dev)
  225. {
  226.   struct FileSysStartupMsg *startup;
  227.   struct DosEnvec *env;
  228.   struct Task *task;
  229.   char temp[FMSIZE+1];
  230.  
  231.   if (!extended) return;  /* user hasn't requested this stuff */
  232.  
  233.   if (!dev)
  234.     {
  235.       printf("\n›3mNo extended device information is available›0m\n");
  236.       return;
  237.     }
  238.  
  239.   printf("\n›3mExtended device information:›0m\n");
  240.   printf("(note: the following information is not consistently\n");
  241.   printf("    used by all AmigaDOS devices, and therefore may be\n");
  242.   printf("    partially or wholly invalid for this particular device)\n\n");
  243.  
  244.   get_bcplname(temp,dev->dn_Handler);
  245.   printf("FS handler:\t%s\n",temp);
  246.  
  247.   if (dev->dn_Task)
  248.     {
  249.       task = dev->dn_Task->mp_SigTask;
  250.       if (task)
  251.         {
  252.           c = task->tc_Node.ln_Name;
  253.           if ( (c) && (*c) )
  254.             {
  255.               strcpy(temp,c);
  256.             }
  257.           else
  258.             {
  259.               goto notaskname;
  260.             }
  261.         }
  262.       else
  263.         {
  264. notaskname:
  265.           strcpy(temp,"›3munknown›0m");
  266.         }
  267.       printf("Task name:\t%s\n",temp);
  268.     }
  269.  
  270.   startup = toAPTR(dev->dn_Startup);
  271.   if (startup)
  272.     {
  273.       get_bcplname(temp,startup->fssm_Device);
  274.       printf("Device name:\t%s\n",temp);
  275.  
  276.       printf("Unit number:\t%lu\n",startup->fssm_Unit);
  277.  
  278.       env = toAPTR(startup->fssm_Environ);
  279.       if (env)
  280.         {
  281.           switch(env->de_DosType)
  282.             {
  283.               case ID_OFS_DISK:
  284.                 {
  285.                   strcpy(temp,"Old file system");
  286.                   break;
  287.                 }
  288.               case ID_FFS_DISK:
  289.                 {
  290.                   strcpy(temp,"Fast file system");
  291.                   break;
  292.                 }
  293.               default:
  294.                 {
  295.                   sprintf(temp,"›3munknown›0m (0x%08X)",env->de_DosType);
  296.                   break;
  297.                 }
  298.             }
  299.           printf("File System:\t%s\n",temp);
  300.  
  301.           printf("Bytes/Block:\t%lu\n",(env->de_SizeBlock * sizeof(long)));
  302.           printf("Surfaces:\t%lu\n",env->de_Surfaces);
  303.           printf("Blocks/Track:\t%lu\n",env->de_BlocksPerTrack);
  304.           printf("Low Cylinder:\t%lu\n",env->de_LowCyl);
  305.           printf("High Cylinder:\t%lu\n",env->de_HighCyl);
  306.           printf("Reserved:\t%lu\n",env->de_Reserved);
  307.           printf("Interleave:\t%lu\n",env->de_Interleave);
  308.           printf("Buffers:\t%lu\n",env->de_NumBuffers);
  309.           printf("Boot Priority:\t%ld\n",env->de_BootPri);
  310.         }
  311.     }
  312. }
  313.  
  314.  
  315. void extra_volinfo(char *volname)
  316. {
  317.   BPTR vlock;
  318.   int rc;
  319.   LONG blocks, bytes;
  320.   char temp[FMSIZE+1];
  321.  
  322.   vlock = Lock(name,ACCESS_READ);
  323.   if (!vlock) return;
  324.   rc = Info(vlock,info);
  325.   UnLock(vlock);
  326.   if (rc == DOSFALSE) return;
  327.  
  328. /* disk type */
  329.   switch(info->id_DiskType)
  330.     {
  331.       case ID_UNREADABLE_DISK:
  332.         {
  333.           strcpy(temp,"Unreadable");
  334.           break;
  335.         }
  336.       case ID_DOS_DISK:
  337.         {
  338.           strcpy(temp,"Normal DOS disk");
  339.           break;
  340.         }
  341.       case ID_NOT_REALLY_DOS:
  342.         {
  343.           strcpy(temp,"Not DOS");
  344.           break;
  345.         }
  346.       case ID_KICKSTART_DISK:
  347.         {
  348.           strcpy(temp,"Kickstart");
  349.           break;
  350.         }
  351.       default:
  352.         {
  353.           strcpy(temp,"›3munknown›0m");
  354.           break;
  355.         }
  356.     }
  357.   printf("Disk type:\t%s\n",temp);
  358.  
  359. /* disk status */
  360.   switch(info->id_DiskState)
  361.     {
  362.       case ID_WRITE_PROTECTED:
  363.         {
  364.           strcpy(temp,"Write protected (read only)");
  365.           break;
  366.         }
  367.       case ID_VALIDATING:
  368.         {
  369.           strcpy(temp,"Not validated (currently unusable)");
  370.           break;
  371.         }
  372.       case ID_VALIDATED:
  373.         {
  374.           strcpy(temp,"Read/Write (validated)");
  375.           break;
  376.         }
  377.       default:
  378.         {
  379.           strcpy(temp,"›3munknown›0m");
  380.           break;
  381.         }
  382.     }
  383.   printf("Status:\t\t%s",temp);
  384.   if (info->id_InUse == DOSTRUE) printf(" (disk is busy)");
  385.   printf("\n");
  386.  
  387. /* disk capacity info */
  388.   printf("Bytes/Block:\t%ld\n",info->id_BytesPerBlock);
  389.   printf("Soft errors:\t%ld\n",info->id_NumSoftErrors);
  390.  
  391.   blocks = info->id_NumBlocks;
  392.   bytes = (blocks * info->id_BytesPerBlock);
  393.   printf("Capacity:\t%ld blocks;\t%ld bytes\n",blocks,bytes);
  394.  
  395.   blocks = info->id_NumBlocksUsed;
  396.   bytes = (blocks * info->id_BytesPerBlock);
  397.   printf("Space used:\t%ld blocks;\t%ld bytes\n",blocks,bytes);
  398.  
  399.   blocks = (info->id_NumBlocks - blocks);
  400.   bytes = (blocks * info->id_BytesPerBlock);
  401.   printf("Space free:\t%ld blocks;\t%ld bytes\n",blocks,bytes);
  402. }
  403.  
  404.  
  405. void show_volinfo(char *name, struct DeviceList *vol)
  406. {
  407.   register struct DeviceNode *dev;
  408.   char temp[FMSIZE+1];
  409.  
  410.   printf("Volume \"%s\" is ",name);
  411.   dev = find_obj(vol->dl_Task,DLT_DEVICE);
  412.   if (!dev)
  413.     {
  414.       printf("not mounted\n");
  415.       printf("Additional information is unavailable\n");
  416.       return;
  417.     }
  418.  
  419.   bstrcpy(temp,dev->dn_Name);
  420.   strcat(temp,":");
  421.   printf("mounted on device \"%s\"\n",temp);
  422.  
  423.   extra_volinfo(name);
  424.  
  425.   extended_devinfo(dev);
  426. }
  427.  
  428.  
  429. void show_fileinfo(char *name, BPTR lock, struct FileInfoBlock *fib)
  430. {
  431.   char temp[FMSIZE+1];
  432.  
  433.   getpath(lock,temp);
  434.   if (!strchr(temp,':')) strcat(temp,":");
  435.   printf("Full path name: \"%s\"\n",temp);
  436.  
  437.   printf("File size: %lu bytes\n",fib->fib_Size);
  438.  
  439.   show_datestamp(&fib->fib_Date);
  440.  
  441.   show_protection(fib->fib_Protection);
  442.  
  443.   if (fib->fib_Comment[0])
  444.     {
  445.       printf("Comment: %s\n",fib->fib_Comment);
  446.     }
  447.  
  448.   printf("\n");
  449.   c = strchr(temp,':');
  450.   *++c = '\0';
  451.   show_volinfo(temp,(struct DeviceList *)lookup_name(temp));
  452. }
  453.  
  454.  
  455. void show_dirinfo(char *name, BPTR lock, struct FileInfoBlock *fib)
  456. {
  457.   char temp[FMSIZE+1];
  458.  
  459.   getpath(lock,temp);
  460.   if (!strchr(temp,':')) strcat(temp,":");
  461.   for(c = temp; *c; c++);
  462.   c--;
  463.   if ( (*c != ':') && (*c != '/') ) strcat(temp,"/");
  464.   printf("Full path name: \"%s\"\n",temp);
  465.  
  466.   show_datestamp(&fib->fib_Date);
  467.  
  468.   if (fib->fib_DirEntryType > 0) show_protection(fib->fib_Protection);
  469.  
  470.   printf("\n");
  471.   c = strchr(temp,':');
  472.   *++c = '\0';
  473.   show_volinfo(temp,(struct DeviceList *)lookup_name(temp));
  474. }
  475.  
  476.  
  477. void show_devinfo(char *name, struct DeviceNode *dev)
  478. {
  479.   struct DeviceList *dl;
  480.   char temp[FMSIZE+1];
  481.  
  482.   if (!dev->dn_Task)
  483.     {
  484.       printf("non-filing system device\n");
  485.       get_bcplname(temp,dev->dn_Handler);
  486.       printf("FS handler:\t%s\n",temp);
  487.       return;
  488.     }
  489.   printf("filing system device\n");
  490.  
  491.   dl = (struct DeviceList *)find_obj(dev->dn_Task,DLT_VOLUME);
  492.   if (!dl)
  493.     {
  494.       printf("There is no volume presently mounted in this device\n");
  495.     }
  496.   else
  497.     {
  498.       bstrcpy(temp,dl->dl_Name);
  499.       strcat(temp,":");
  500.       printf("\nVolume \"%s\" is currently mounted in this device\n",temp);
  501.       extra_volinfo(temp);
  502.     }
  503.  
  504.   extended_devinfo(dev);
  505. }
  506.  
  507.  
  508. void show_assigninfo(char *name, struct DeviceList *dev)
  509. {
  510.   BPTR alock;
  511.   struct FileLock *fl;
  512.   struct DeviceList *dl;
  513.   char volname[FMSIZE+1];
  514.  
  515. /* get volume name */
  516.   alock = dev->dl_Lock;
  517.   if (!alock) return;
  518.   fl = toAPTR(alock);
  519.   dl = toAPTR(fl->fl_Volume);
  520.   bstrcpy(volname,dl->dl_Name);
  521.  
  522. /* check if volume is mounted */
  523.   dl = (struct DeviceList *)find_obj(fl->fl_Task,DLT_VOLUME);
  524.  
  525. /* no, just show name of volume */
  526.   if (!dl)
  527.     {
  528.       printf("It identifies a directory or file on volume: \"%s:\"\n",volname);
  529.       printf("The volume is not mounted\n");
  530.       printf("Additional information is unavailable\n");
  531.       return;
  532.     }
  533.  
  534. /* yes, get all info for this object */
  535.   Examine(alock,fib);
  536.   if (fib->fib_DirEntryType > 0)  /* a directory */
  537.     {
  538.       if (strsam(fib->fib_FileName,volname))
  539.         {
  540.           printf("It identifies the root directory of volume: \"%s\"\n",fib->fib_FileName);
  541.           fib->fib_DirEntryType = 0L; /* prevents show_dirinfo() from
  542.                                       ** attempting to display protection
  543.                                       ** bits for this volume name */
  544.         }
  545.       else
  546.         {
  547.           printf("It identifies a directory: \"%s\"\n",fib->fib_FileName);
  548.         }
  549.       show_dirinfo(fib->fib_FileName,alock,fib);
  550.     }
  551.   else
  552.     {
  553.       printf("It identifies a file: \"%s\"\n",fib->fib_FileName);
  554.       show_fileinfo(fib->fib_FileName,alock,fib);
  555.     }
  556. }
  557.  
  558.  
  559. void handle_switch(char *arg)
  560. {
  561.   switch(toupper(arg[1]))
  562.     {
  563.       case 'X': /* extended info on */
  564.         {
  565.           extended = 1;
  566.           break;
  567.         }
  568.       default:
  569.         {
  570.           printf("\nUnrecognized switch: \"%s\"\n",arg);
  571.           break;
  572.         }
  573.     }
  574. }
  575.  
  576.  
  577. void add_colon(char *name)
  578. {
  579.   printf("\nYou forgot to postfix a colon to this name.\n");
  580.   printf("Remember that AmigaDOS will not recognize it\n");
  581.   printf("without a colon attached.\n");
  582.   strcat(name,":");
  583. }
  584.  
  585.  
  586. main(int argc, char *argv[])
  587. {
  588.   int arg;
  589.   register struct DeviceNode *dn;
  590.  
  591.   if (argc == 0) exit(0); /* We're men here, we don't do Workbench! */
  592.  
  593.   printf("\n›3;33;40mFile System Info v1.0 ›0m- created by Ray Lambert\n");
  594.  
  595.   if (argc == 1)
  596.     {
  597.       printf("\nCLI usage: fsi [-x] <name>\n");
  598.       printf("    where <name> is the name of any filing system object\n");
  599.       printf("    and -x is an optional switch to request extended\n");
  600.       printf("    device information\n\n");
  601.       exit(0);
  602.     }
  603.  
  604.  
  605. /* get memory for a struct InfoData */
  606.   info = AllocMem(sizeof(struct InfoData),MEMF_PUBLIC|MEMF_CLEAR);
  607.   if (!info)
  608.     {
  609. nomem:
  610.       printf("\nSorry, not enough memory\n\n");
  611.       clean_exit(20);
  612.     }
  613.  
  614.  
  615. /* get memory for a struct FileInfoBlock */
  616.   fib = AllocMem(sizeof(struct FileInfoBlock),MEMF_PUBLIC|MEMF_CLEAR);
  617.   if (!fib) goto nomem;
  618.  
  619.  
  620. /* disable DOS requesters */
  621.   Forbid();
  622.   process = (struct Process *)FindTask(NULL);
  623.   old_pr_WindowPtr = process->pr_WindowPtr;
  624.   process->pr_WindowPtr = (void *)-1L;
  625.   Permit();
  626.  
  627.  
  628. /* look at all args */
  629.   for(arg = 1; arg < argc; arg++)
  630.     {
  631.  
  632.       if (argv[arg][0] == '-')  /* a switch */
  633.         {
  634.           handle_switch(argv[arg]);
  635.           continue;
  636.         }
  637.  
  638.       strcpy(name,argv[arg]);
  639.  
  640.     /* is this a device/volume/assign name? */
  641.       c = strchr(name,':');
  642.       if (c) c++;
  643.       if ( (!c) || (!*c) )
  644.       /* apparently not, but it may still be... */
  645.         {
  646.           dn = lookup_name(name);
  647.           if (!dn) goto tryalock; /* not in the device list, trying getting a lock */
  648.           if ( (!c) || (*c) ) add_colon(name);
  649.           switch(dn->dn_Type)
  650.             {
  651.               case DLT_DEVICE:
  652.                 {
  653.                   printf("\n\"%s\" is a ",name);
  654.                   show_devinfo(name,dn);
  655.                   break;
  656.                 }
  657.               case DLT_VOLUME:
  658.                 {
  659.                   printf("\n\"%s\" is a volume name\n",name);
  660.                   show_volinfo(name,(struct DeviceList *)dn);
  661.                   break;
  662.                 }
  663.               case DLT_DIRECTORY:
  664.                 {
  665.                   printf("\n\"%s\" is a logical name\n",name);
  666.                   show_assigninfo(name,(struct DeviceList *)dn);
  667.                   break;
  668.                 }
  669.               default:
  670.                 {
  671.                   goto noinfo;
  672.                 }
  673.             }
  674.         }
  675.       else
  676.     /* it is (presumably) a file or directory */
  677.         {
  678. tryalock:
  679.           lock = Lock(name,ACCESS_READ);
  680.  
  681.           if (!lock)
  682.             {
  683. noinfo:
  684.               if (strsam("NIL",name)) add_colon(name);
  685.               if (strsam("NIL:",name))
  686.                 {
  687.                   printf("\n\"NIL:\" is a 'fake' filing system device which\n");
  688.                   printf("is normally used to suppress unwanted output\n");
  689.                   printf("from programs which write to the CLI...\n");
  690.                   printf("Additional information is unavailable\n");
  691.                   continue;
  692.                 }
  693.               printf("\nSorry, can't get any information for \"%s\"\n",argv[arg]);
  694.               continue;
  695.             }
  696.  
  697.           Examine(lock,fib);
  698.           if (fib->fib_DirEntryType > 0)  /* a directory */
  699.             {
  700.               printf("\n\"%s\" is a directory name\n",name);
  701.               show_dirinfo(name,lock,fib);
  702.             }
  703.           else
  704.             {
  705.               printf("\n\"%s\" is a file name\n",name);
  706.               show_fileinfo(name,lock,fib);
  707.             }
  708.           UnLock(lock);
  709.           lock = NULL;
  710.         }
  711.     }
  712.  
  713.   clean_exit(0);
  714. }
  715.