home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / filutl / chatt.arc / CHATT.C next >
C/C++ Source or Header  |  1988-04-12  |  19KB  |  614 lines

  1.  
  2. /***********************************************************************/
  3. /* CHATT.C - This program was typed from the April 1988 issue of Micro */
  4. /* Systems Journal from an article by Mark Zeiger titled "Using MS-DOS */
  5. /* Functions in C". CHATT changes file mode to or from READ/ONLY,      */
  6. /* SYSTEM, or HIDDEN interactively by first displaying current mode    */
  7. /* and then asking if you wish a change to be made. Wildcards may be   */
  8. /* used. CHATT.C and its functions was written in Eco-88 by the author */
  9. /* and modified by the typist for Quick-C. This file will not work     */
  10. /* with Eco-88. I (the typist) couldn't get the program to work with   */
  11. /* some of Mr. Zeigler's code and made some changes, most notably in   */
  12. /* the format of the way the prompt to change is handled and in the    */
  13. /* method of calling INT 21h. If you have the article you can compare  */
  14. /* the code, if you don't it won't matter anyhow; the program works    */
  15. /* and will compile as is with Quick C in the small memory model. Note */
  16. /* that getzf.asm must be assembled and the two object modules must    */
  17. /* be linked with chatt.obj linked first. Any changes I made are re-   */
  18. /* flected in the remarks and code below.                              */
  19. /***********************************************************************/
  20.  
  21. /*
  22. Command line parameters
  23.  
  24. C>chatt [control] [path\]filename [[path\]filename ...]
  25.  
  26. "Control" parameter specifies whether attribute should be turned on or off
  27. and whether you should be asked if change is to be done.
  28.  
  29. R specifies READ/ONLY, H specifies hidden, and S specifies SYSTEM. If path is 
  30. not specified, default directouy is used. Filename may include wildcard 
  31. characters. A directory alone is expanded to all files in directory (i.e.
  32. test = test\*.* if test is a directory).
  33.  
  34. A "+N" in control parameter will allow changes to be made without asking. If 
  35. no control parameter is specified then attributes will be displayed only.
  36. A "-N" in control paremeter is equivilent to omitting this paremeter, you will 
  37. be asked to confirm each change.
  38. The control parameter takes the form of the string +/-R | +/-H | +/-S | +/-N in 
  39. any order, and the control string must start with a + or -)
  40.  
  41.    examples:
  42.  
  43. 1. chatt +r-h+s+n abc\*.com   Changes all .COM files in directory "abc" to 
  44.                               READ/ONLY and SYSTEM andtakes away HIDDEN 
  45.                               attribute. No prompt is issued for each file
  46.  
  47. 2. chatt -s+h abc\*.* d:*.asm Changes all files in directory "abc" to HIDDEN 
  48.                               and all .ASM files in current directory of drive 
  49.                               "D" to HIDDEN. Also takes away SYSTEM attribute 
  50.                               of those files. You will be prompted if change 
  51.                               is to be done.
  52.  
  53. 3. chatt -s+h abc d:*.asm     Same as above
  54.  
  55. 4. chatt \abc\*.com           Lists attributes of all .COM files in dir \abc.
  56. */
  57.  
  58. #include <dos.h>           /* declares REGS */
  59. #include <string.h>
  60. #include <ctype.h>         /* for toupper(), etc. */
  61.  
  62. #define TRUE 1
  63. #define FALSE 0
  64. #define NULL 0
  65.  
  66. /* DOS call parameters */
  67.  
  68. #define  MSDOSS   0x0021   /* MS-DOS interrupt */
  69. #define  SETMODE  0x4301   /* subfunction number in AL */
  70. #define  GETMODE  0x4300   /* subfunction number in AL */
  71. #define  SEARCHF  0x4e00   /* search for first occurance */
  72. #define  SEARCHN  0x4f00   /* search for next occurance */
  73. #define  SETDMA   0x1a00   /* set DMA address */
  74. #define  CONNIO   0x06     /* raw console I/O */
  75. #define  CARRYF   0x0001   /* position of carry flag */
  76. #define  ZEROF    0x0040   /* position of zero flag */
  77.  
  78. /* file attribute bits for function 43H */
  79.  
  80. #define  RO       0x0001
  81. #define  HIDDEN   0x0002
  82. #define  SYSTEM   0x0004
  83. #define  VOLUME   0x0008
  84. #define  SUBDIR   0x0010
  85.  
  86. /* errors from setmode and search commands */
  87.  
  88. #define  NOFILE   0x0002   /* file not found */
  89. #define  NOPATH   0x0003   /* path not found */
  90. #define  NOACCESS 0x0005   /* access denied */
  91. #define  NOFILES  0x0012   /* no more files on SEARCHN */
  92.  
  93. /* BIOS interrupt 10h constants */
  94.  
  95. #define  CURR_VIDEO  0x0f   /* */
  96. #define  READ_CURSOR 0x03   /* */
  97. #define  SET_CURSOR  0x02   /* */
  98. #define  VIDEO_INT   0x0010   /* */
  99.  
  100. /* structure filled in by SEARCHF command of MS-DOS */
  101.  
  102. struct DMA {
  103.    char reserved[21];   /* reserved by MS-DOS */
  104.    char attribute;      /* attribute of file */
  105.    unsigned time;
  106.    unsigned date;
  107.    unsigned size_L;     /* file size - low word */
  108.    unsigned size_h;     /* file size - high word */
  109.    char fname[13];      /* parsed name of file */
  110. };
  111.  
  112. union REGS inregs, outregs;
  113. struct SREGS segregs;
  114.  
  115. /*---------------------------- MAIN -----------------------------------*/
  116.  
  117. main(argc, argv)
  118. int argc;
  119. char *argv[];
  120. {
  121.    char atton_off;      /* is it a + or - */
  122.    char path[64];       /* holds path name up to 64 bytes */
  123.    char new_arg[64];    /* might hold subdir concat with \*.*    */
  124.    int bslpos;          /* position of last backslash in path name */
  125.    struct DMA dmabuf;   /* used for MS-DOS search function */
  126.    int i;               /* used to count parameters */
  127.    int carryf;          /* carry flag for MS-DOS calls */
  128.  
  129.    void fpe(), bad_syntax(), pfname();
  130.    void set_att_masks(), change_att();
  131.    void concat();
  132.    void display_type();
  133.    char lastchar();
  134.    int getpath();
  135.  
  136.    unsigned att_on_mask, att_off_mask; /* masks to set/reset attributes */
  137.    unsigned ask;                       /* prompt for each change ???    */
  138.    int start_file;               /* which argv[] contains 1st. path\file */
  139.    int display_att_only;         /* flag to display attributes only */
  140.  
  141.    if (argc < 2)
  142.       bad_syntax();
  143.    atton_off = argv[1][0];
  144.    if (atton_off != '+' && atton_off != '-') {
  145.       start_file = 1;                  /* argv[1] is a path */
  146.       display_att_only = TRUE;
  147.    }
  148.    else {
  149.       start_file = 2;               /* argv[1] is control parameter */
  150.       display_att_only = FALSE;     /* and argv[2] starts files */
  151.       if (argc < 3)                 /* must have at least one file */
  152.          bad_syntax();
  153.       set_att_masks(argv[1], &att_on_mask, &att_off_mask, &ask);
  154.    }
  155.  
  156. /* set the MS-DOS DMA x'fer address to dmabuf */
  157.  
  158.    segread(&segregs);      /* put seg regs in structure (need DS) */
  159.    inregs.x.ax = SETDMA;              /* AH = funct 1Ah */
  160.    inregs.x.dx = (unsigned) &dmabuf;
  161.    int86x(MSDOSS, &inregs, &outregs, &segregs);
  162.  
  163.    for(i = start_file; i < argc; i++) {
  164.       /* changes a subdirectory name to subdir\*.* or changes root to
  165.          full search (\ -> \*.*) or changes to drivespec:\*.* (c: -> c:\*.*)
  166.       */
  167.       if (lastchar(argv[i]) == ':') {
  168.          concat(new_arg, argv[i], "\\*.*");
  169.          argv[i] = new_arg;
  170.       }
  171.       else if(lastchar(argv[i]) == '\\') {
  172.          concat(new_arg, argv[i], "*.*");
  173.          argv[i] = new_arg;
  174.       }
  175.       else {      /* Subdirectory ? or file ?   */
  176.          segread(&segregs);      /* put seg regs in structure (need DS) */
  177.          inregs.x.ax = GETMODE;  /* AX = 4300 (Int 43h, 00 = 'get' mode */
  178.          inregs.x.dx = (unsigned) argv[i];
  179.          int86x(MSDOSS, &inregs, &outregs, &segregs);
  180.          carryf = outregs.x.cflag;
  181.          if(outregs.x.cx == SUBDIR) {
  182.             concat(new_arg, argv[i], "\\*.*");
  183.             argv[i] = new_arg;
  184.          }
  185.       }
  186.  
  187.       bslpos = getpath(argv[i], path);
  188.  
  189.       fpe("\n====================> ");
  190.       fpe(" >====================\n\n");
  191.  
  192.       segread(&segregs);      /* put seg regs in structure (need DS) */
  193.       inregs.x.ax = SEARCHF;             /* AH = funct 4Eh */
  194.       inregs.x.dx = (unsigned) argv[i];
  195.       inregs.x.cx = 0x001f;              /* search for file with any attri- */
  196.       int86x(MSDOSS, &inregs, &outregs, &segregs);  /* bute except archive */
  197.       carryf = outregs.x.cflag;
  198.  
  199.       if ((carryf & CARRYF) == CARRYF) {
  200.          if ((outregs.x.ax == NOFILES) || (outregs.x.ax == NOPATH )) {
  201.             fpe("\n");
  202.             fpe(argv[i]);
  203.             fpe(" not found\n\n\r");
  204.          }
  205.          else {
  206.             fpe("\nUndefined error\n\007");
  207.             exit(1);
  208.          }
  209.       }
  210.  
  211.       while((carryf & CARRYF) != CARRYF) {
  212.          pfname(path, &(dmabuf.fname[0]), bslpos);
  213.          fpe(path);     /* echo path and/or file */
  214.          display_type(dmabuf.attribute);        /* echo current attribs */
  215.          if ((display_att_only != TRUE) && (dmabuf.attribute != SUBDIR))
  216.             change_att(path, dmabuf.attribute, &att_on_mask,&att_off_mask,&ask);
  217.          else
  218.             fpe("\n");
  219.  
  220.          segread(&segregs);      /* put seg regs in structure (need DS) */
  221.          inregs.x.ax = SEARCHN;  /* AX = 4F00 (4Fh= 'search for next match' */
  222.          int86x(MSDOSS, &inregs, &outregs, &segregs);
  223.          carryf = outregs.x.cflag;
  224.       }
  225.    }        /* end of for loop */
  226.    exit(0);
  227. }           /* end of main */
  228.  
  229. /*-----------Funct set_att_masks---------------------------------------*/
  230. /*
  231.    When it comes time to set or reset attributes, directory attribute byte 
  232.    will be "or"ed with "att_on_mask" to set attribute bits we want on and then 
  233.    "and"ed with "att_off_mask" to reset attributes we want off. This is done 
  234.    in function "change_att()".
  235. */
  236.  
  237. void set_att_masks(att_string, att_on_mask, att_off_mask, ask)
  238. char *att_string;
  239. unsigned *att_on_mask, *att_off_mask, *ask;
  240. {
  241.  
  242. int sub_str();
  243.  
  244.    *att_on_mask = 0;
  245.    *att_off_mask = 0xffff;
  246.  
  247.    if (sub_str(att_string, "+R"))  *att_on_mask |= RO;
  248.    else if (sub_str(att_string, "-R"))  *att_off_mask ^= RO;
  249.  
  250.    if (sub_str(att_string, "+H"))  *att_on_mask |= HIDDEN;
  251.    else if (sub_str(att_string, "-H"))  *att_off_mask ^= HIDDEN;
  252.  
  253.    if (sub_str(att_string, "+S"))  *att_on_mask |= SYSTEM;
  254.    else if (sub_str(att_string, "-S"))  *att_off_mask ^= SYSTEM;
  255.  
  256.    if (sub_str(att_string, "+N"))  *ask = FALSE;
  257.    else *ask = TRUE;
  258. }
  259.  
  260. /*----------------------function sub_str()-----------------------------*/
  261.  
  262. /*
  263.    Returns TRUE if substring (+R,-H, etc.) is contained in first string or
  264.    FALSE if not. Case insensitive.
  265. */
  266.  
  267. int sub_str(main_string, sub_string)
  268. char *main_string, *sub_string;
  269. {
  270.  
  271. static int *pos, temp;
  272. int len_main, len_sub;
  273. int xstrcomp();
  274.  
  275.    pos = &temp;         /* point to any int to initialize */
  276.    len_main = strlen(main_string);
  277.    len_sub = strlen(sub_string);
  278.    if (len_main < len_sub)
  279.       return FALSE;
  280.    for (*pos = 0; *pos < len_main - len_sub + 1; (*pos)++)
  281.       if (xstrcomp(main_string, sub_string, pos, len_sub))
  282.          return TRUE;
  283.    return FALSE;
  284. }
  285.  
  286. /*------------------------function xstrcomp()--------------------------*/
  287.  
  288. /*
  289.    Compares "sub_string" substring with "main_string" starting at "pos" 
  290.    with a length of "length". Case insensitive.
  291. */
  292.  
  293. int xstrcomp(main_string, sub_string, pos, length)
  294. char *main_string, *sub_string;
  295. int *pos, length;
  296. {
  297.  
  298. int j;
  299.  
  300.    for( j = 0; j < length; (*pos)++, j++) {
  301.       if(toupper(*(main_string + *pos)) != toupper(*(sub_string + j))) {
  302.          if(!(isalpha(*(main_string + *pos))))     /* if not alphabetic */
  303.             (*pos)++;
  304.          return FALSE;
  305.       }
  306.       continue;
  307.    }
  308.    return TRUE;
  309. }
  310.  
  311. /*------------------------function concat()----------------------------*/
  312.  
  313. /*
  314.    Concatenates "first" string followed by "second" and puts result in "new" 
  315.    string.
  316. */
  317.  
  318. void concat(new, first, second)
  319. char *new, *first, *second;
  320. {
  321.  
  322.    while(*first != (char) NULL) {
  323.       *new = *first;
  324.       first++;
  325.       new++;
  326.    }
  327.    while(*second != (char) NULL) {
  328.       *new = *second;
  329.       second++;
  330.       new++;
  331.    }
  332.    *new = (char) NULL;
  333. }
  334.  
  335. /*--------------------------function lastchar()------------------------*/
  336.  
  337. /*
  338.    returns the last (non-null) character of a string
  339. */
  340.  
  341. char lastchar(string)
  342. char *string;
  343. {
  344.    return string[(strlen(string) - 1)];
  345. }
  346.  
  347. /*-------------------------function getpath()--------------------------*/
  348.  
  349. /*
  350.    Puts path name (without file name) in array "path" and returns position of 
  351.    last backslash. If no path name (default dir) then puts NULL string in 
  352.    path[] and sets bslpos to -1. A path is also considered to be a drive 
  353.    specification without a subdirectory, hence the test for(:).
  354. */
  355.  
  356. int getpath(argvs, path)
  357. char *argvs, *path;
  358. {
  359.  
  360. static char ch;
  361. register int i;
  362. int bslpos = -1;
  363.  
  364.    for(i = 0; (ch = argvs[i]) != (char) NULL; i++) {
  365.       if (ch == '\\' || ch == ':')
  366.          bslpos = i;
  367.    }
  368.    if (bslpos != -1)
  369.       for(i = 0; i <= bslpos; i++)
  370.          path[i] = argvs[i];
  371.    path[i] = NULL;
  372.    return bslpos;
  373. }
  374.  
  375. /*--------------------------function pfname()--------------------------*/
  376.  
  377. /*
  378.    Puts file name (from dmabuf.fname) at end of path (which contains 
  379.    pathname).  bslpos is position of last backslash.
  380. */
  381.  
  382. void pfname(path, fname, bslpos)
  383. char *path, *fname;
  384. int bslpos;
  385. {
  386.  
  387. register i, j;
  388. static char ch;
  389.  
  390.    j = bslpos != -1 ? bslpos + 1 : 0;
  391.    for (i = 0; (ch = fname[i]) != (char) NULL; i++, j++)
  392.       path[j] = fname[i];
  393.    path[j] = (char) NULL;
  394. }
  395.  
  396. /*------------------function change_att()------------------------------*/
  397.  
  398. /*
  399.    Changes attribute of file after query (unless "ask" is FALSE). No change if 
  400.    subdirectory.
  401. */
  402.  
  403. void change_att(fullname, att, att_on_mask, att_off_mask, ask)
  404. char *fullname;
  405. unsigned att, *att_on_mask, *att_off_mask, *ask;
  406. {
  407.  
  408. static unsigned char answer;
  409. static int carryf;
  410. char input();
  411. void echo(), state_err(), fpe();
  412.  
  413.    if (*ask == TRUE) {
  414.       fpe("     Change? ");
  415.       do {
  416.          answer = input();
  417.          if (answer == 3)        /* abort if ^C */
  418.             exit(2);
  419.       }
  420.       while ((toupper(answer) != 'Y') && (toupper(answer) != 'N'));
  421.       echo(answer);
  422.    }
  423.    else answer = 'Y';         /* force answer without asking */
  424.    if(toupper(answer) == 'Y') {
  425.       segread(&segregs);      /* put seg regs in structure (need DS) */
  426.       inregs.x.ax = SETMODE;  /* AX = 4301, (Int 43h, 01 = 'set' mode  */
  427.       inregs.x.cx = att | *att_on_mask;
  428.       inregs.x.cx = (inregs.x.cx & *att_off_mask) & 0x00ff;
  429.       inregs.x.dx = (unsigned) fullname;
  430.       int86x(MSDOSS, &inregs, &outregs, &segregs);
  431.       carryf = outregs.x.cflag;
  432.       if ((carryf & CARRYF) == CARRYF)
  433.          state_err(outregs.x.ax);
  434.    }
  435.    fpe("\n");
  436. }
  437.  
  438. /*----------------------function state_err-----------------------------*/
  439. /*
  440.    States error returned in AX register after change attribute call. Will 
  441.    abort if no file or incorrect path since this indicates program can not 
  442.    start. Will not abort on "Access Denied" since this can happen with 
  443.    subdirectories.
  444. */
  445.  
  446. void state_err(rax)
  447. unsigned rax;
  448. {
  449.  
  450. void fpe();
  451.  
  452.    rax = rax & 0x00ff;        /* isolate AL register */
  453.    if (rax == NOFILE)
  454.       fpe("\nFile not found.\007");
  455.    if (rax == NOPATH)
  456.       fpe("\nPath not found.\007");
  457.    if (rax == NOACCESS)
  458.       fpe("    Access denied.\007");
  459.  
  460.    if ( !((rax == NOFILE) || (rax == NOPATH) || (rax == NOACCESS )))
  461.       fpe("\n\007Undefined error.");
  462.    if (rax == NOACCESS) {
  463.       fpe("\n");
  464.       exit(1);
  465.    }
  466. }
  467.  
  468. /*---------------------function bad_syntax()---------------------------*/
  469. /*
  470.    Indicates command line error and shows correct syntax.
  471. */
  472.  
  473. void bad_syntax()
  474. {
  475.  
  476. void fpe();
  477.  
  478.    fpe("\nIncorrect syntax\007");      /* 361 is octal for +/-  */
  479.    fpe("\n\nUsage: chatt [\361R | \361S | \361H | \361N] file file ...\007");
  480.    fpe("\n\n\361R = R/O on/off.");
  481.    fpe("\n\361S = SYSTEM on/off.");
  482.    fpe("\n\361H = HIDDEN on/off.");
  483.    fpe("\n+N = Change without asking.");
  484.    fpe("\nControls may be strung together.");
  485.    fpe("\n   i.e. +R-H+N+S or -S+R+N");
  486.    fpe("\n\nIf no control, then file attributes are displayed only.\n");
  487.    exit(1);
  488. }
  489.  
  490. /*------------------function display_type()----------------------------*/
  491. /*
  492.    Displays attribute of file. Called from pfname() which gets attribute from 
  493.    dmabuf.attribute.
  494. */
  495.  
  496. void display_type(att)
  497. char att;
  498. {
  499.  
  500. void fpe(), cursor_col();
  501.  
  502.    cursor_col(30);
  503.    if ((att & 0x1f) == 0)
  504.       fpe(" NORMAL");
  505.    else {
  506.       if ((att & RO) == RO)
  507.          fpe(" READ/ONLY");
  508.       if ((att & HIDDEN) == HIDDEN)
  509.          fpe(" HIDDEN");
  510.       if ((att & SYSTEM) == SYSTEM)
  511.          fpe(" SYSTEM");
  512.       if ((att & VOLUME) == VOLUME)
  513.          fpe(" VOLUME");
  514.       if ((att & SUBDIR) == SUBDIR)
  515.          fpe(" SUB-DIRECTORY");
  516.    }
  517. }
  518.  
  519. /*------------------------function fpe()-------------------------------*/
  520. /*
  521.    Prints string using MS-DOS console I/O function. This is because fputs() 
  522.    library function is incredibly slow. Also tests for console input and 
  523.    pauses if any key is pressed. Will resume when any key is pressed again.
  524. */
  525.  
  526. void fpe(string)
  527. char *string;
  528. {
  529.  
  530. static unsigned char ch, ch1;
  531. void echo();
  532. char input();
  533.  
  534.    while (( ch = *string++) != (unsigned char) NULL)
  535.       if (ch != '\n')
  536.          echo(ch);
  537.       else {
  538.          echo('\015');        /* ASCII CR */
  539.          echo(ch);            /* now do linefeed */
  540.       }
  541.       if ((ch1 = input()) != (unsigned char) NULL)
  542.          if (ch1 == 3)
  543.             exit(1);
  544.          else while ((ch1 = input()) == (unsigned char) NULL)
  545.             if (ch1 == 3)
  546.                exit(1);
  547. }
  548.  
  549.  
  550. /*---------------function cursor_col()---------------------------------*/
  551. /*
  552.    Places cursor at column "col" of cursor line using BIOS int 10h
  553. */
  554.  
  555. void cursor_col(col)
  556. int col;
  557. {
  558.  
  559. static unsigned char current_page;
  560.  
  561.    inregs.h.ah = CURR_VIDEO;     /* Get current page funct */
  562.    int86(VIDEO_INT, &inregs, &outregs);
  563.    current_page = outregs.h.bh;
  564.  
  565.    inregs.h.ah = READ_CURSOR;    /* Read cur pos funct call */
  566.    inregs.h.bh = current_page;   /* Page number */
  567.    int86(VIDEO_INT, &inregs, &outregs);
  568.  
  569.    inregs.h.ah = SET_CURSOR;     /* Set cur pos funct call */
  570.    inregs.h.bh = current_page;   /* Page number */
  571.    inregs.h.dh = outregs.h.dh;   /* row (y) from fumct 3 above */
  572.    inregs.h.dl = outregs.h.dl;   /* col (x) from funct 3 above */
  573.    int86(VIDEO_INT, &inregs, &outregs);
  574. }
  575.  
  576. /*-----------------------function input()------------------------------*/
  577. /*
  578.    Will return NULL if not character ready, or character. Does not wait for 
  579.    input. Uses MS-DOS function AH = 6 for raw console I/O. Can not be used for 
  580.    function key input.
  581. */
  582.  
  583. char input()
  584. {
  585.  
  586. static unsigned int zerof;    /* used for return of zero flag */
  587. extern int getzf();
  588.  
  589.    inregs.h.ah = CONNIO;              /* AH = funct 06h */
  590.    inregs.h.dl = 0xff;           /* input subfunction */
  591.    int86x(MSDOSS, &inregs, &outregs, &segregs);
  592.    zerof = getzf();        /* ASM routine to get zero flag */
  593.  
  594.    if ((zerof & ZEROF) == ZEROF)
  595.       return (char) NULL;
  596.    else
  597.       return outregs.h.al;
  598. }
  599.  
  600. /*-----------------------function echo()-------------------------------*/
  601. /*
  602.    Echos character to console using MS-DOS function AH = 6.
  603. */
  604.  
  605. void echo(ch)
  606. unsigned char ch;
  607. {
  608.  
  609.    inregs.h.ah = CONNIO;
  610.    inregs.h.dl = ch;
  611.    int86(MSDOSS, &inregs, &outregs);
  612. }
  613.  
  614.