home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / mbug / mbug051.arc / DISKNUM.C < prev    next >
Text File  |  1979-12-31  |  10KB  |  307 lines

  1. /* DISKNUM.C   87-05-21   by  Simon Gerraty
  2.  * This program will read a file such as MAST.CAT produced by ncat.com,
  3.  * and produce a numerically sorted list of disk names 
  4.  */ 
  5. #include <local.h>
  6. /* local.h contains the following commonly used #include directives
  7. #include <stdio.h>
  8. #include <cpm.h>
  9. #include <ctype.h>
  10.  * commonly used #defines
  11. #define TRUE 1
  12. #define FALSE 0
  13. #define uchar unsigned char
  14. /* CONTROL CODES FOR ADM-3A recognised by MICROBEE  */
  15. #define bel()    putchar('\007') /* Ring bel, what else? */
  16. #define cls()    putchar('\032') /* Clear Screen & Home */
  17. #define home()   putchar('\036') /* Home Cursor */
  18. #define curup()  putchar('\013') /* Cursor up one line */
  19. #define curdn()  putchar('\012') /* Cursor down one line */
  20. #define curlef() putchar('\010') /* Cursor left one char */
  21. #define currit() putchar('\014') /* Cursor right one char */
  22.  * end of local.h
  23.  */
  24. /*
  25.  * defines here help make changes easier and help remove 'magic numbers' from
  26.  * the rest of the program
  27.  */
  28. #define PROGNAME "DiskNum"
  29. #define VER      "1"
  30. #define SUBVER   "01"
  31. #define DATE     "22-May-87"
  32. #define COPYRIT  "Copyright (C) 1987, Simon Gerraty"
  33. #define PURPOSE  ""
  34. #define MAX 128
  35. #define MAX_MISS 60
  36. /* global variables (those defined outside of any function) are not 
  37.  * generally good practice.  functions should be written such that all
  38.  * values they care about are either handed to them as arguments, or 
  39.  * generated internally.  See for example strcspn() at the end of the
  40.  * listing.  Functions written this way can be compiled, stored in a
  41.  * library and never looked at again, yet they are available as building
  42.  * blocks for other programs.  Anyway globals are bad news, but I am
  43.  * lazy, so I use them for flags etc.
  44.  */
  45.  static char dbug = FALSE;  
  46.  static char verbose = FALSE;  
  47.  static char ascending = TRUE;
  48. /* each C function is defined with an argument list between ()s. Many 
  49.  * functions have no arguments eg foo(). Every program has a function 
  50.  * main() which has two arguments. Argv is an array of pointers to the 
  51.  * string arguments (supplied on the command line). Argc is an integer and 
  52.  * is equal to the number of arguments pointed to by argv
  53.  */
  54. main(argc, argv)   
  55. int argc;         
  56. char *argv[];
  57. {
  58. /* 
  59.  * all variables that will be used in this function must be declared 
  60.  * before use.  NOTE:  that functions that return other than ints must 
  61.  * also be declared before use - or the compiler will tell you about it!
  62.  */
  63.  char *malloc(), *s, *ln,  *l;
  64.  char finished, changed, *fgets();
  65.  FILE *fopen(), *inpfp, *outfp;
  66.  short n, m, i, cnt, iter;
  67.  char *inf, *outf, *line[200]; 
  68. /* announce ourselves to the world */
  69.  signon();
  70. /* process any flags on the command line */
  71.  while (--argc > 0 && (*++argv)[0] == '-')
  72.  {
  73.   s = argv[0]+1;
  74.   switch (*s)
  75.   {
  76.    case 'R':              
  77.     ascending = FALSE;    
  78.     break;                
  79.    case 'V': 
  80.     verbose = TRUE;     
  81.     break;  
  82.    case 'D': 
  83.     dbug = TRUE;        
  84.     break;  
  85.    default:
  86.     fprintf(stderr, "%s: illegal option %c\n", PROGNAME, *s);
  87.     break;
  88.   }
  89.  }
  90. /* restore argc & argv to compensate for while() above */
  91.  argc++;
  92.  argv--;
  93. /* if we have not got the right number of arguments then die! */
  94.  if (argc < 3)
  95.   usage();
  96. /* open the files we need be sure that all goes well */
  97.  if ((inpfp = fopen((inf = *++argv), "r")) == (FILE *)NULL)
  98.   errexit(1, "can't open input file");
  99.  if ((outfp = fopen((outf = *++argv), "w")) == (FILE *)NULL)
  100.   errexit(1, "can't open output file");
  101.  if (verbose)
  102.   printf("Scanning Input file %s\n", inf);
  103.  cnt = iter = 0;
  104.  finished = FALSE;
  105.  while (!(finished))
  106.  {
  107.   ln = malloc(MAX); 
  108. /* allocate memory for the next input line */
  109.   l = fgets(ln, MAX, inpfp);
  110.   if ((m = strtest(ln, ".FRE"))) /* test if it's one we want */
  111.   {
  112.    finished = FALSE;
  113. /* we are only interested in the part after the ',' */
  114.    m = strcspn(ln, ","); 
  115. /* move the pointer to just after the ',' */
  116.    ln += ++m; 
  117.    line[i++] = ln;
  118. /* line[] is an array of pointers to char strings here we put a pointer to
  119.  * the current line (ln) into the array. Using the array of pointers will 
  120.  * make sorting much more simple - as we shall see.
  121.  */
  122.   }
  123.   else
  124.    if (cnt++ > MAX_MISS)  
  125.     finished = TRUE;
  126. /* since we know that the lines we want are a contiguous block  that may
  127.  * have an exception list before it, we allow to read MAX_MISS lines that 
  128.  * do not meet our criteria before stopping. after all we don't want to 
  129.  * read all of an 80k catalogue file!
  130.  */
  131.  } 
  132. /* close the input file, since we are finished with it */
  133.  fclose(inpfp);  
  134. /* if we think we read more than 200 lines with .FRE in them, then
  135.  * something probably went wrong - in any case we have not allowed for
  136.  * sorting more than 200 names, so exit gracefully!
  137.  */
  138.  if ((m = --i) > 200) 
  139.   errexit(1, "$&'#)$(!  I don't understand it!");
  140.  if (verbose)
  141.   printf("OK, Now sorting the list of names\n");
  142. /* now we want to sort our list of names, which remember are pointed to by  
  143.  * the array of pointers line[].  Generally the algorithm used will take 
  144.  * less than n iterations to sort n items. So we continue to shuffle the  
  145.  * list while we detect that it is changing, and while the number of  
  146.  * iterations is less than 2*n.  The following code, passes pointers to
  147.  * two successive pointers in the array line[] to the function swap(). 
  148.  * We pass pointers to pointers so that swap() can 'swap' them if required. 
  149.  * In C the only way for a called function to affect any objects (eg 
  150.  * variables) other than globals or ones local to the called function, is 
  151.  * if it is passed pointers to the objects.
  152.  */
  153. /* be sure to set the initial condition for the while loop */
  154.  changed = TRUE;
  155.  while ((changed) && iter++ < (m 2))
  156.  {
  157.   changed = FALSE;
  158.   for (i=0; i < m; i++)    
  159.    if ((n = swap(&line[i], &line[(i + 1)])) == TRUE) 
  160.     changed = TRUE;
  161.  }
  162.  if (verbose)
  163.   printf("Finished! %d sorted in %d iterations\n", m, iter);
  164.  fprintf(outfp, "List of disk names from %s\n\n", inf);
  165.  for (i = 0; i <= m; i++)
  166.  {
  167. /* C has no built in IO support, but standard library functions such as 
  168.  * printf() and fprintf() allow very sophisticated output formatting.
  169.  */ 
  170.   fprintf(outfp, " %2d\t%15s", i, line[i]);
  171. /* if there is a gap in the numeric sequence between i and (i+1) then 
  172.  * print a blank line
  173.  */
  174.   if ((n = isgap(line[i], line[(i + 1)])) == TRUE)
  175.    fprintf(outfp, "\n");
  176.  }
  177. /* now close the output file 'cos we're nearly done! */
  178.  fclose(outfp);
  179. } /* end of function main() */
  180. /* now the functions called in main must be supplied, either in this
  181.  * file or in others which will be compiled separately and 'linked' with 
  182.  * the compiled code from this file.
  183.  */
  184. /* swap() takes 2 pointers to pointers, compares the contents of the
  185.  * strings they point to, and swaps them (the pointers) if the strings
  186.  * are out of sequence.  The strings always remain in the memory to which
  187.  * they were read, and the pointers (which will control the order in
  188.  * which the strings are printed) are moved about. Shuffling two byte
  189.  * pointers is a lot quicker (and easier) than shuffling 80 character
  190.  * strings!
  191.  */ 
  192. swap(s1, s2) 
  193. char **s1, **s2;  
  194.   char swp, *tmp; 
  195.   short n1, n2;
  196.   n1 = value(*s1);
  197.   n2 = value(*s2);
  198.   if (n1 > n2)
  199.     swp = (ascending) ? TRUE : FALSE;
  200.   else
  201.     swp = (ascending) ? FALSE : TRUE;  
  202.   if (swp)
  203.   {
  204.     tmp = *s1;
  205.     *s1 = *s2;
  206.     *s2 = tmp;
  207.     n1 = TRUE;
  208.   }
  209. /* let the calling function know whether we swapped or not */
  210.   return(swp);
  211. }
  212. /* isgap() takes two string pointers, and checks whether the gap
  213.  * between them is not more than one.
  214.  */
  215. isgap(s1, s2)
  216. char *s1, *s2;
  217. {
  218.  short n1, n2;
  219.  n1 = value(s1);
  220.  n2 = value(s2);
  221.  if (ascending)
  222.   n1 =  (++n1 < n2) ? TRUE : FALSE;
  223.  else
  224.   n1 = (n1 > ++n2) ? TRUE : FALSE;
  225.  return(n1);
  226. }
  227. /* take a string, find the numeric bit at the end,
  228.  * and return it as an int
  229.  */
  230. value(s)
  231. char *s;
  232. {
  233.  short l;
  234.  s += (l = strcspn(s, "."));  /* skip over everything       */
  235.  l = atoi(++s);               /* upto and including the '.' */
  236.  return(l);
  237. }
  238. /* the name says it all! */
  239. signon()
  240. {
  241.  fprintf(stderr, "\n\n%s  V%s.%s  %s", PROGNAME, VER, SUBVER, DATE);
  242.  fprintf(stderr, "  %s\n%s\n\n", COPYRIT, PURPOSE);
  243. }
  244. /* same here */
  245. usage()
  246. {
  247.  bel();
  248.  fprintf(stderr, "\nUsage:\t\t%s ", PROGNAME);
  249.  fprintf(stderr, "<input file> <output file>");
  250.  exit(1);
  251. }
  252. /* this one is really only useful in environments which support multi-
  253.  * tasking, and/or spawned processes eg Unix, MS-DOS etc The idea is to 
  254.  * allow the program to exit with a return code that will signal to the 
  255.  * parent process, whether things worked ok or whether 
  256.  * it died with it's leg in the air.
  257.  */
  258. errexit(l, s)
  259. short l;
  260. char *s;
  261. {
  262.  fprintf(stderr, "%s: %s\n", PROGNAME, s);
  263.  exit(l);
  264. }
  265. /* test s1 for the presence of s2
  266.  */
  267. strtest(s1, s2)
  268. char *s1, *s2;
  269. {
  270.  char c;
  271.  short l, i = 0;
  272.  l = strlen(s2);
  273.  while ((c = *s1++) && i < l)
  274.  {
  275.   if (c == s2[i])
  276.    i++;
  277.   else
  278.    i=0;
  279.  }
  280.  return((i < l) ? FALSE : TRUE); /* did we find it or not? */
  281. }
  282. /* return the length of the 1st span of s1   that contains none of the 
  283.  * chars in s2.     This function would normally be written for performance 
  284.  * and placed in a library, and never looked at again!
  285.  */
  286. strcspn(s1, s2)                    
  287. char *s1, *s2;                     
  288. {                                  
  289.  char c, finished;                 
  290.  short i, l = 0;                   
  291.  finished = FALSE;                 
  292.  while (!(finished))               
  293.  {                                 
  294.   c = s1[l++];                     
  295.   for (i=0; i < strlen(s2); i++)   
  296.    if (c == s2[i])                 
  297.     finished = TRUE;               
  298.  }                                 
  299.  return(--l);                      
  300. }                                  
  301. /* That's all folks! */            
  302. 2)
  303. char *s1, *s2;
  304. {
  305.  char c;
  306.  short l, i