/* AT.C
**  Changes the attributes of one or more files as specified from the
**  command line.
**  AT filename1 +R would add the readonly attribute to filename1.
**  AT filename1 +RH would add both readonly and hidden attributes to filename1.
**  AT filename1 -R would remove only the readonly attribute.
**  AT filename1 -RH would remove both readonly and hidden attributes.
**  AT filename1 +r filename2 -h does the following:
**   adds readonly to filename1,
**   removes hidden from filename2
**  By Kirby Devore 2/91
**  MSC V5.1
**   cl /AL /W3 at.c   
*/

#include <stdio.h>
#include <dos.h>      /* for _dos_() */
#include <process.h>  /* for exit() */
#include <string.h>
#include <malloc.h>   /* for malloc() */

#define READO 0x01 /* obtained from dos.h and shortened */
#define HIDE  0x02
#define SYS   0x04
#define ARCH  0x20

struct llist /* structure for linked list */
{
   char fname[13];
   struct llist *link;
};

/* function prototypes */
char **dirlist(char *);              
void get_file_attrs(char *);
void set_file_attrs(char *,unsigned);
void reset_file_attrs(char *,unsigned);
void main(int,char **); /* yes, even main to maintain ANSI standards */

void get_file_attrs(filename)
char *filename;
{
   unsigned attr;

   if(_dos_getfileattr(filename,&attr)) /* non-portable - insert TC equivalent fileattr call here */
      printf("READ failed\n");          
   {
      printf("%s %s %s %s %s\n",filename,
            (attr&SYS)?"SYSTEM":"",
            (attr&HIDE)?"HIDDEN":"",
            (attr&READO)?"READON":"",
            (attr&ARCH)?"ARCHIV":"");
   }
}

void set_file_attrs(filename,attr)
char *filename;
unsigned attr;
{
   unsigned attrib;

   if(!_dos_getfileattr(filename,&attrib)) /* non-portable insert TC equivalent fileattr call here */
   {
      attrib|=attr; /* attrib is bit-wise ORed by attr */
      if(_dos_setfileattr(filename,attrib)) /* non-portable insert TC equivalent fileattr call here */
          printf("SET failed.\n");
   }
}

void reset_file_attrs(filename,attr)
char *filename;
unsigned attr;
{
   unsigned attrib;

   if(!_dos_getfileattr(filename,&attrib)) /* non-portable insert TC equivalent fileattr call here */
   {
      attrib&=~attr; /* attrib is bit-wise ANDed with the complement of attr */
      if(_dos_setfileattr(filename,attrib)) /* non-portable insert TC equivalent fileattr call here */
          printf("RESET failed.\n");
   }
}

char **dirlist(arglist)
char *arglist;
{
   char **list;
   struct find_t name;
   struct llist *link_list;
   struct llist *base;
   int nfiles,i;

   nfiles=0;
   if(!_dos_findfirst(arglist,READO|HIDE|SYS|ARCH,&name)) /* non-portable insert TC equivalent fileattr call here */
   {
      link_list=(struct llist *)malloc(sizeof(struct llist));
      if(link_list!=NULL)
      {
         base=link_list;
         strcpy(link_list->fname,name.name);
         nfiles++;
         link_list->link=(struct llist *)malloc(sizeof(struct llist));
         link_list=link_list->link;
         while(!_dos_findnext(&name)) /* non-portable insert TC equivalent fileattr call here */
         {
            strcpy(link_list->fname,name.name);
            nfiles++;
            link_list->link=(struct llist *)malloc(sizeof(struct llist));
            link_list=link_list->link;
         }
         link_list->link=NULL;
         link_list=base;
         list=(char **)malloc((nfiles+1)*sizeof(char *));
         for(i=0;i<nfiles;i++)
         {   
            list[i]=link_list->fname;
            link_list=link_list->link;
         }
         list[i]=NULL;
         return(list);
      }
      else
         return(NULL);
   }
   else
      return(NULL);
}

void main(argc,argv)
int argc;
char **argv;
{
   char *filename;
   char **wildnames;
   int names,ops,wild;

   names=ops=wild=0;
   if(argc>1) /* must be more than one argument to run program */
   {
      if(strcmp(argv[1],"")) /* at least one filename exists */
      {
         if(strcmp(argv[2],"")) /* at least one operation exists */
         {
            for(names=1;names<argc;names+=2)
            {
               wildnames=dirlist(argv[names]);
               if(wildnames!=NULL)
               {
                  wild=0; /* make sure index gets reset */
                  while(wildnames[wild]!=NULL)
                  {
                     filename=wildnames[wild];
                     switch(argv[names+1][0]) /* operation must follow filename */
                     {
                        case '+':
                           for(ops=1;argv[names+1][ops]!=' ' && argv[names+1][ops]!='\0';ops++)
                           {
                              switch(argv[names+1][ops]) /* keeps looping until space or end of line */
                              {
                                 case 'r':
                                 case 'R':
                                    set_file_attrs(filename,READO);
                                    break;
                                 case 'a':
                                 case 'A':
                                    set_file_attrs(filename,ARCH);
                                    break;
                                 case 'h':
                                 case 'H':
                                    set_file_attrs(filename,HIDE);
                                    break;
                                 case 's':
                                 case 'S':
                                    set_file_attrs(filename,SYS);
                                    break;
                              }
                           }
                           break;
                        case '-':
                           for(ops=1;argv[names+1][ops]!=' ' && argv[names+1][ops]!='\0';ops++)
                           {
                              switch(argv[names+1][ops])
                              {
                                 case 'r':
                                 case 'R':
                                    reset_file_attrs(filename,READO);
                                    break;
                                 case 'a':
                                 case 'A':
                                    reset_file_attrs(filename,ARCH);
                                    break;
                                 case 'h':
                                 case 'H':
                                    reset_file_attrs(filename,HIDE);
                                    break;
                                 case 's':
                                 case 'S':
                                    reset_file_attrs(filename,SYS);
                                    break;
                              }
                           }
                           break;
                     }
                     get_file_attrs(filename); /* echo atrs back */
                     wild++;
                  }
               }
               else
               {
                  printf("Insufficient memory to create filelist.\n");
                  exit(0);
               }
            }
         }
         else
         {
            filename=argv[1];
            get_file_attrs(filename);
         }
      }
   }
   else
   {
      printf("Usage:AT filename1 (+ or -)RSAH filename2 (+ or -)RSAH etc..\n");
      printf("where filenamex is any valid DOS filename.ext.\n");
      printf("Wildcards are fully supported.\n");
   }
/* No real need to free up memory here, DOS will do that for us. */
/* but if you don't believe me, run chkdsk before and after to satisfy. */
}
