home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 June / SIMTEL_0692.cdr / msdos / printer / lpr_lpq3.arc / LPR.C < prev    next >
Text File  |  1989-10-04  |  21KB  |  784 lines

  1. /*
  2.    LPR.C -  a utility to allow you to pipe into the DOS PRINT command
  3.                                                     R. Brittain  May 3, 1989
  4.     Spool files are created in directory $SPOOLDIR  (default is C:/SPOOL)
  5.     File creation template is LPR???.$$$ where ??? are filled in with digits
  6.     000-999
  7.  
  8.     This program is
  9.                     Copyright  1989  by Richard Brittain
  10.     Permission is granted to freely use and distribute this program so long as
  11.     this copyright notice is maintained intact.  This software may not be sold.
  12.  
  13.     Revision history:
  14.     1.0     6/18/89 Released to usenet as version 1.0   6/18/89
  15.     1.1     9/19/89    changed exit code for "print.com not loaded" to 2 for use
  16.             in batch files. Fixed bug in prepending directory to given
  17.             filename in root directory, or if drive is specified.  Changed
  18.             wildcard expansion in setargv() to handle "/" in paths, and also
  19.             to treat as wildcards filespecs which have [] but not * or ?, for
  20.             better use with the "wildunix" TSR.
  21.             File specs containing combinations of "." and ".." are now handled
  22.             correctly
  23. */
  24.  
  25. #include <stdio.h>
  26. #include <dir.h>
  27. #include <string.h>
  28. #include <stdlib.h>
  29. #include <io.h>
  30. #include <alloc.h>
  31. #include <ctype.h>
  32. #include <dos.h>
  33. #include <errno.h>
  34. #include <time.h>
  35.  
  36. #define SPOOLDIR  "C:\\SPOOL"    /* where we will put the temporary print files */
  37. #define SPOOLMASK "LPR???.$$$"   /* template for the print file names */
  38. #define CONTROLZ '\032'
  39. #define FF         '\014'
  40. #define    DOSPRINT     1
  41. #define MULTIPLEX    0x2F
  42. #define QELEN        64             /* length in bytes of a print queue entry */
  43. #define MAGIC        '*'
  44.  
  45. /* function prototypes */
  46.  
  47. char         *getfilename(char*);
  48. int              index(char*, char*);
  49. int           delete_files(char*);
  50. char         *mkformat(char*, int*);
  51. int          strcount(char*, char);
  52. int            check(char*, char*);
  53. int            size(char*);
  54. int            print_loaded(void);
  55. int            print_file(char*);
  56. char far     *print_queue(void);
  57. char        *localcopy(char far *);
  58. char        *localcopy2(char far *);
  59. void        release_print_queue(void);
  60. int            lp_stdin(int, int);
  61. int         add_time_to_queue(void);
  62. char far     *farcopy2(char far *, char *);
  63. char far     *strcpyfar2(char far *, char far *);
  64. int            print_job_rush(int);
  65. int            resolve_path(char *,char *);
  66. char        *parent(char *);
  67. char        *endchar(char *);
  68. char         *catdir(char *, char*);
  69. char         *translit(char *, char, char);
  70.  
  71. main(argc,argv)
  72. int     argc;
  73. char    **argv;
  74. {
  75. /*    Main program processes command line arguments and options only       */
  76. /*  The exit code is 0 is no errors are encountered, 1 if any argument   */
  77. /*  is in error.  All arguments are processed even if some give an error */
  78. /*  Exit code is 2 if PRINT.COM is not loaded                            */
  79.  
  80.     char    *p, pathname[64] ;
  81.     int        i, files=0, exitcode=0, verbose=0, badargs=0, rush=0, job, result;
  82.     char    version[]="1.1";
  83.     char    copyright[]="Copyright  1989  by  Richard Brittain";
  84.     char    usage[]="Usage: command | lpr [-v] [-h] [-p] [file [-] ....]";
  85.     char    helptext[]="\
  86. Spool files are created in C:\SPOOL or in the directory given in SPOOLDIR\n\
  87. Filename arguments (wildcards may be used) are added to print queue.\n\
  88. Argument \"-\" means stdin (if redirected or piped). Stdin is assumed if no\n\
  89. arguments are given.\n\
  90. Options:\n\
  91.     -h display help message\n\
  92.     -v verbose mode\n\
  93.     -p priority mode - add job(s) to head of print queue\n\n\
  94. PRINT should be loaded prior to using lpr";
  95.     char    permiss[]="Permission is granted to freely use and distribute "
  96.                       "this program";
  97.  
  98. /*  check that the print spooler is loaded */
  99.     if (!print_loaded()) {
  100.         printf("lpr: DOS print spooler is not loaded\n");
  101.         exit(2);
  102.     }
  103.  
  104. /* process the command line arguments */
  105.     for (i=1; i<argc; i++) {
  106.         p = argv[i];
  107.         if ( (*p == '-') && (strlen(p) > 1)) {
  108.             do {
  109.                 p++;
  110.                 switch(*p) {
  111.                     case '\0':
  112.                         break;
  113.                     case 'h':  /* help */
  114.                         printf("LPR Version %s of %s\n",version,__DATE__);
  115.                         printf("                 %s\n\n",copyright);
  116.                         printf("%s\n\n",usage);
  117.                         printf("%s\n\n",helptext);
  118.                         printf("%s\n",permiss);
  119.                         exit(1);
  120.                     case 'v':  /* verbose mode */
  121.                         verbose = 1;
  122.                         break;
  123.                     case 'p':  /* priority */
  124.                         rush = 1;
  125.                         break;
  126.                     default:
  127.                         printf("lpr: unrecognised option %c\n",*p);
  128.                         badargs = 1;
  129.                 }
  130.             } while (*p);
  131.         } else {
  132.             files++;
  133.             if (strcmp(p,"-") == 0) {
  134.                 if (isatty(fileno(stdin))) {
  135.                     printf("lpr: cannot take input from a terminal\n");
  136.                     exitcode = 1;
  137.                     badargs  = 1;
  138.                 } else {
  139.                     exitcode = lp_stdin(verbose,rush);
  140.                 }
  141.             } else {
  142.                 /* turn the specified file name into a full path */
  143.                 result = resolve_path(pathname,p);
  144.                 if (result == -1) {
  145.                     printf("lpr: invalid drive specified\n");
  146.                     continue;
  147.                 }
  148.  
  149.                 if (print_file(pathname) < 0) {
  150.                     switch (_doserrno) {
  151.                         case ENOMEM:
  152.                             printf("lpr: spool queue is full: failed to add %s\n",
  153.                             pathname);
  154.                             break;
  155.                         case EACCES:
  156.                             printf("lpr: cannot print a directory: %s\n",
  157.                             pathname);
  158.                             break;
  159.                         default:
  160.                             printf("lpr: %s ",pathname);
  161.                             perror("");
  162.                             break;
  163.                     }
  164.                     exitcode = 1;
  165.                 } else {
  166.                     job = add_time_to_queue();
  167.                     if (rush) print_job_rush(job);
  168.                     if (verbose) {
  169.                         if (rush)
  170.                             printf("lpr: adding %s to head of print queue\n",pathname);
  171.                         else
  172.                             printf("lpr: adding %s to print queue\n",pathname);
  173.                     }
  174.                 }
  175.             }
  176.         }
  177.     }
  178.  
  179. /*     If we have not done any files, do stdin, if not a tty */
  180. /*     check that our input is not a terminal - this isn't really an error but */
  181. /*  it isn't useful to allow it and may be confusing */
  182.     if (files == 0) {
  183.         if (isatty(fileno(stdin))) {
  184.             printf("lpr: cannot take input from a terminal\n");
  185.             badargs  = 1;
  186.             exitcode = 1;
  187.         } else {
  188.             exitcode = lp_stdin(verbose,rush);
  189.         }
  190.     }
  191.     release_print_queue();
  192.     if (badargs) {
  193.         printf("%s\n",usage);
  194.         if (verbose) printf("\n%s\n",helptext);
  195.     }
  196.     exit(exitcode);
  197. }
  198.  
  199. int lp_stdin(int verbose, int rush)
  200. {
  201. /*  Routine to take stdin and print it via the DOS PRINT program        */
  202. /*  Makes a file in a spool directory and then passes the name to PRINT */
  203.     FILE    *tfp;
  204.     char    spoolname[13],  spooldir[64]=SPOOLDIR;
  205.     char    cwd[MAXDIR], *sp, sdrive[MAXDRIVE];
  206.     int        c, i, lastc, fileok=1, cdisk, sdisk, job;
  207.     struct    dfree  dtable;
  208.  
  209. /*  find out our cwd and default drive */
  210.     cdisk = getdisk();
  211.     getcwd(cwd, MAXDIR);
  212.  
  213. /*  now change to the spool directory, possibly on another drive */
  214.     if ((sp=getenv("SPOOLDIR")) != NULL) strcpy(spooldir,sp);
  215.     fnsplit(spooldir,sdrive,NULL,NULL,NULL);
  216.     sdisk = toupper(sdrive[0])-'A';
  217.     if (setdisk(sdisk) < sdisk+1) perror("lpr");
  218.     if (chdir(spooldir) != 0) {
  219.         printf("lpr: cannot access spool directory %s \n",spooldir);
  220.         setdisk(cdisk);
  221.         return(1);
  222.     }
  223.  
  224. /*  get a unique filename for the spool file */
  225.     strcpy(spoolname, getfilename(SPOOLMASK));
  226.  
  227.     if ((tfp=fopen(spoolname,"w"))==NULL) {
  228.         printf("lpr: cannot create spool file %s \n",spoolname );
  229.         setdisk(cdisk);
  230.         chdir(cwd);
  231.         return(1);
  232.     }
  233.  
  234. /* copy stdin to the new spool file */
  235.     c = fgetchar();
  236.     if (feof(stdin)) {
  237.         fileok = 0;
  238.         if (verbose) printf("lpr: input file is empty\n") ;
  239.     }
  240.     rewind(stdin);
  241.     for (;;) {
  242.         lastc =  c;
  243.         c = fgetchar();
  244.         if (feof(stdin)) {
  245.             break ;
  246.         } else if (ferror(stdin)) {
  247.             printf("lpr: cannot read stdin\n") ;
  248.             fileok = 0;
  249.             break ;
  250.         }
  251.         if (putc(c,tfp) == EOF) {
  252.             printf("lpr: write error on spool file: ") ;
  253.             getdfree( 0, &dtable);
  254.             if (dtable.df_avail == 0)
  255.                 printf("disc is full\n");
  256.             else
  257.                 perror("");
  258.             fileok = 0;
  259.             break ;
  260.         }
  261.     }
  262.  
  263.     if (!fileok) {
  264.         fclose(tfp);
  265.     } else {
  266. /*      we don't want to copy a trailing formfeed because PRINT will add one */
  267.         if (lastc == FF ) {
  268.             fseek( tfp, ftell(tfp)-1, SEEK_SET );
  269.             putc( CONTROLZ, tfp);
  270.         }
  271.         fclose(tfp);
  272.  
  273. /*      now pass this name to print for insertion into the queue */
  274.         i = strlen(spooldir)-1;
  275.         if (spooldir[i] != '\\') strcat(spooldir,"\\");
  276.         strcat(spooldir,spoolname);
  277.         if (print_file(spooldir) < 0) {
  278.             if (_doserrno == ENOMEM)
  279.                 printf("lpr: spool queue is full: failed to add %s\n",spooldir);
  280.             else
  281.                 perror("lpr");
  282.         } else {
  283.             job = add_time_to_queue();
  284.             if (rush) print_job_rush(job);
  285.             if (verbose) {
  286.                 if (rush)
  287.                     printf("lpr: adding %s (stdin) to head of print queue\n",spooldir);
  288.                 else
  289.                     printf("lpr: adding %s (stdin) to print queue\n",spooldir);
  290.             }
  291.         }
  292.     }
  293.  
  294. /*  we are done, but first clean up the spool directory by deleting any */
  295. /*  files matching SPOOLMASK that do NOT appear in the print queue  */
  296.     delete_files(SPOOLMASK);
  297.  
  298. /*  cd back to where we came from and quit */
  299.     setdisk(cdisk);
  300.     chdir(cwd);
  301.     return(0);
  302. }
  303.  
  304. char *getfilename(pattern)
  305. char *pattern;
  306. /* generate a unique filename in the current directory,                 */
  307. /* based on the given pattern.  Patterns look like bbbb???.eee          */
  308. /* where bbbb is a "base name" of 1-7 characters, ??? will be replaced  */
  309. /* by sequential numbers, up to all 9's, and eee is an optional         */
  310. /* constant extension.  Thus "LPR???.$$$" will generate the lowest      */
  311. /* file in the range LPR000.$$$ to LPR999.$$$ not currently existing    */
  312. /* Turning the pattern into a format for sprintf is done by mkformat()  */
  313. /* The maximum allowable number of "?" is 4, any more are ignored        */
  314. {
  315.     int     i, maxtries;
  316.     char    format[30];
  317.     static    char    filename[13];
  318.  
  319.     if (strcpy(format,mkformat(pattern, &maxtries))==NULL) return(NULL);
  320.     i = 0;
  321.     do {
  322.         sprintf( filename, format, i);
  323.         i++;
  324.         if ( i > maxtries) return( NULL ) ;
  325.     } while (access(filename,0) == 0) ;
  326.  
  327.     return ((char *)filename);
  328. }
  329.  
  330. int index(str,sub)
  331. char    *str, *sub;
  332. {
  333. /*  return index into <str> of first occurrence of <sub>, if any */
  334.     char *i;
  335.     if ((i=strstr(str,sub)) != NULL)
  336.         return(i-str+1);
  337.     else
  338.         return(0);
  339. }
  340.  
  341. int delete_files( mask )
  342. char *mask;
  343. {
  344. /*  The current print queue is obtained by a call to print_queue, and this */
  345. /*  is checked against the files matching the filename mask, so that we do */
  346. /*  not delete files in the queue.  This routine returns the number of     */
  347. /*  files deleted.                                                           */
  348.  
  349.     int     i=0, n=0;
  350.     char    *m, far *ps, far *p;
  351.     struct    ffblk fblock;
  352.  
  353.     if (*(ps=print_queue()) == NULL) return(0) ;
  354.     /* the print queue contains fixed length (64 bytes) entries */
  355.  
  356.     /*  make an upper case copy of the mask for comparing  */
  357.     m = strupr(strdup(mask)) ;
  358.     if (findfirst(m, &fblock, 0) == 0) {
  359.         p = ps;
  360.         i = 0;
  361.         while (*p != NULL) {  /* cycle through all entries in queue */
  362.             if (strstr(localcopy(p),fblock.ff_name) != NULL) {
  363.                 /* this file is in the queue - don't search further */
  364.                 i++;
  365.                 break;
  366.             }
  367.             p += QELEN;
  368.         }
  369.         if ((i == 0) && check(m,fblock.ff_name)) {
  370.             if (remove(fblock.ff_name) == 0) n++ ;
  371.         }
  372.     }
  373.     while (findnext(&fblock) == 0) {
  374.         p = ps;
  375.         i = 0;
  376.         while (*p != NULL) {  /* cycle through all entries in queue */
  377.             if (strstr(localcopy(p),fblock.ff_name) != NULL) {
  378.                 /* this file is in the queue - don't search further */
  379.                 i++;
  380.                 break;
  381.             }
  382.             p += QELEN;
  383.         }
  384.         if ((i == 0) && check(m,fblock.ff_name)) {
  385.             if (remove(fblock.ff_name) == 0) n++ ;
  386.         }
  387.     }
  388.     release_print_queue();
  389.     return(n);
  390. }
  391.  
  392. char *localcopy(char far *s)
  393. /* Make a copy of a string pointed to by a far pointer.  The space is mallocd */
  394. {
  395.     char far *p, *l, *r ;
  396.     int         i=0 ;
  397.     p = s;
  398.     while (*p++ != NULL) { i++; }
  399.     r = l = (char *)malloc(i+1);
  400.     p = s;
  401.     while (*p != NULL) { *l++ = *p++ ;}
  402.     *l = '\0';
  403.     return(r);
  404. }
  405.  
  406. char *localcopy2(char far *s)
  407. /* Make a copy of a string pointed to by a far pointer             */
  408. /* Look for magic character and optionally copy second string also */
  409. /* Never copy more than QELEN total characters                       */
  410. {
  411.     char far *p, *l, *r ;
  412.     int         i=0 ;
  413.     p = s;
  414.     while (*p++ != NULL) { i++; }
  415.     if (*(++p) == MAGIC) {
  416.         i++;
  417.         while (*p++ != NULL) { if (i++ >= QELEN-1) break; }
  418.     }
  419.     r = l = (char *)malloc(i+1);
  420.     p = s;
  421.     i = 0;
  422.     while (*p != NULL) { *l++ = *p++; i++;}
  423.     *l = '\0';
  424.     if (*(++p) == MAGIC) {
  425.         l++;
  426.         i++;
  427.         while (*p != NULL) { *l++ = *p++;  if (i++ >= QELEN) break; }
  428.         *l = '\0';
  429.     }
  430.     return(r);
  431. }
  432.  
  433. int strcount( s, c)
  434. char *s, c;
  435. {
  436.     int i,j=0;
  437.     for (i=0;i<strlen(s);i++) if (*(s+i) == c) j++ ;
  438.     return(j);
  439. }
  440.  
  441. char  *mkformat(pattern,maxtries)
  442. char  *pattern;
  443. int   *maxtries;
  444. {
  445.     int     ln, lp, lf, lb, i;
  446.     char    fmtspec[12], ext[4];
  447.     static     char format[20];
  448.  
  449.     lp = strlen(pattern);
  450.     lf = index(pattern,".")-1 ;
  451.     lb = index(pattern,"?")-1 ;
  452.     lf = (lf > 0) ? lf : lp ;
  453.     ln = min(4,strcount(pattern,'?'));
  454.     if (ln < 1) return(NULL) ;
  455.     strncpy(format,pattern,lb);
  456.     format[lb] = '\0';
  457.     sprintf(fmtspec,"%s%d%s","%0",ln,"d.") ;
  458.     strcat (format, fmtspec);
  459.     if (lf+1 < lp) {
  460.         strncpy(ext,&pattern[lf+1],3);
  461.         ext[3] = '\0';
  462.     } else {
  463.         strcpy( ext,"   ");
  464.     }
  465.     strcat (format, ext);
  466.  
  467.     *maxtries = 10;
  468.     for (i=2;i<=ln;i++) *maxtries = *maxtries*10;
  469.     *maxtries--;
  470.  
  471.     return(format);
  472. }
  473.  
  474. int size(s)
  475. char *s;
  476. {
  477. /*  return the length of string <s> after stripping trailing blanks */
  478.     int l;
  479.     l = strlen(s);
  480.     while ((*(s+l-1) == ' ') && (l>0)) l-- ;
  481.     return(l);
  482. }
  483.  
  484. int check(mask, str)
  485. char *mask, *str;
  486. {
  487. /*  verify that <mask> and <str> are the same, except that for */
  488. /*  each "?" in <mask> there is a corresponding DIGIT in <str>    */
  489.  
  490.     int i,len;
  491.     char c;
  492.  
  493.     if ((len=size(mask)) != size(str))     return(0);
  494.  
  495.     for  (i=0; i<len; i++) {
  496.         if ((c=*(mask+i)) == '?') {
  497.             if (!isdigit(*(str+i))) return(0) ;
  498.         } else {
  499.             if (*(str+i) != c) return(0) ;
  500.         }
  501.     }
  502.     return(1) ;
  503. }
  504.  
  505. int print_loaded()
  506. {
  507. /* find out if the resident portion of the DOS print spooler is loaded */
  508.     union REGS  regs;
  509.     regs.h.ah = DOSPRINT;
  510.     regs.h.al = 0;
  511.     int86(MULTIPLEX,®s,®s);
  512.     if (regs.h.al == 0xFF)
  513.         return(1);        /* print is installed */
  514.     else
  515.         return(0);        /* print is not installed */
  516. }
  517.  
  518. int print_file(filename)
  519. char *filename;
  520. {
  521. /* submit a filename to PRINT for addition to the queue */
  522. /* The returned value is the value returned in AL by PRINT */
  523. /*    AL = 158 - filename accepted and now printing */
  524. /*    AL = 1     - filename accepted and added to queue */
  525. /*  if the carry flag indicates an error, -1 is returned */
  526.  
  527.     static struct submit_packet {
  528.         unsigned char level;
  529.         char far       *p;
  530.     } submit ;
  531.     union     REGS      regs;
  532.  
  533.     regs.h.ah = DOSPRINT;
  534.     regs.h.al = 1;                      /* AL=1 means we are submitting a file          */
  535.     submit.level = 0;                    /* don't know what this is for but it is needed */
  536.     submit.p = MK_FP(_DS, filename);    /* make a far pointer to the file name          */
  537.     regs.x.dx = (char near *)&submit;   /* put the segment offset into dx               */
  538.     /* NOTE: the above line gives a pointer warning */
  539.     int86(MULTIPLEX,®s,®s);       /* the ds register is set already               */
  540.     if (regs.x.cflag != 0)
  541.         return(-1);                        /* error */
  542.     else
  543.         return (regs.h.al);
  544. }
  545.  
  546. char far *print_queue()
  547. {
  548. /* Get a far pointer to the start of the list of files in the print queue    */
  549. /* This also freezes the queue (and suspends operation of the print spooler) */
  550. /* until another call is made to any PRINT function */
  551.     union     REGS     regs;
  552.     struct    SREGS     sregs;
  553.     regs.h.ah = DOSPRINT;
  554.     regs.h.al = 4;
  555.     int86x(MULTIPLEX,®s,®s,&sregs);
  556.     return (MK_FP(sregs.ds, regs.x.si));
  557.     /* DS:SI is a far pointer to the queue */
  558. }
  559.  
  560. void release_print_queue()
  561. {
  562. /*  This is a do-nothing print function that serves only to release the print queue */
  563.     union     REGS     regs;
  564.     regs.h.ah = DOSPRINT;
  565.     regs.h.al = 5;
  566.     int86(MULTIPLEX,®s,®s);
  567. }
  568.  
  569. int add_time_to_queue()
  570. {
  571. /*  Find the last entry in the print queue, and add the current time in */
  572. /*  hh:mm behind the filename, if there is room.  Return the job number */
  573.     char    queuentry[QELEN], far *fp;
  574.     int        i=0, l;
  575.     time_t     now;
  576.  
  577.     if (*(fp=print_queue()) == NULL) return(i);
  578.     /* find last entry in queue */
  579.     while (*fp) { i++; fp += QELEN; };
  580.     fp -= QELEN;
  581.  
  582.     strcpy(queuentry, localcopy(fp));
  583.     l = strlen(queuentry);
  584.     if (QELEN - l >= 11) {        /* put the current time after the filename, if room */
  585.         time(&now);
  586.         queuentry[l+1] = MAGIC;
  587.         strncpy(&queuentry[l+2], ctime(&now)+11, 8);
  588.         queuentry[l+10] = '\0';
  589.         farcopy2(fp, queuentry);
  590.     }
  591.     return(i);
  592. }
  593.  
  594. char far *farcopy2(char far *dest, char *src)
  595. /* Copy a string from a near pointer into a far pointer (already allocated) */
  596. /* If the magic character follows, copy a second string                     */
  597. /* Never copy more than QELEN total characters                                */
  598. {
  599.     char far *p, *s ;
  600.     int     i=0;
  601.     p = dest;
  602.     s = src;
  603.     while (*s != NULL) { *p++ = *s++; if (i++ >= QELEN) break; }
  604.     *p = '\0';
  605.     if (*(++s) == MAGIC) {
  606.         p++;
  607.         while (*s != NULL) { *p++ = *s++; if (i++ >= QELEN) break; }
  608.         *p = '\0';
  609.     }
  610.     return(dest);
  611. }
  612.  
  613. char far *strcpyfar2(char far *dest, char far *src)
  614. /* Copy a string from one far pointer into another      */
  615. /* If the magic character follows, copy a second string */
  616. /* Never copy more than QELEN total characters            */
  617. {
  618.     char far *p, far *s ;
  619.     int     i=0;
  620.     p = dest;
  621.     s = src;
  622.     while (*s != NULL) { *p++ = *s++; if (i++ >= QELEN) break; }
  623.     *p = '\0';
  624.     if (*(++s) == MAGIC) {
  625.         p++;
  626.         while (*s != NULL) { *p++ = *s++; if (i++ >= QELEN) break; }
  627.         *p = '\0';
  628.     }
  629.     return(dest);
  630. }
  631.  
  632. int print_job_rush(job)
  633. int job;
  634. {
  635. /*  Get the print queue, look for entry <job>, and place it at the head */
  636. /*  of the queue */
  637. /*  Return -1 if <job> is invalid else return 0 */
  638.     char far *ps, *l;
  639.     int        i;
  640.  
  641.     if (*(ps=print_queue()) != NULL) {
  642.         i = 1;
  643.         while (*ps != NULL) {
  644.             if (i == job) break;
  645.             ps += QELEN;
  646.             i++;
  647.         }
  648.         if (*ps == NULL) return(-1) ;  /* there were not that many jobs in queue */
  649.         if (i <= 2) return(0) ;        /* job is already at head */
  650.         l = localcopy2(ps);               /* make a copy of entry <job> */
  651.         for (i=1; i<=job-2; i++) {     /* copy each job into the following slot */
  652.             strcpyfar2(ps, ps-QELEN);
  653.             ps -= QELEN;
  654.         }
  655.         farcopy2(ps,l);                   /* put the priority job into slot 2 */
  656.         return(0);
  657.     } else {
  658.         return(-1);                    /* no jobs in queue */
  659.     }
  660. }
  661.  
  662. int resolve_path(pathname,p)
  663. char *pathname, *p;
  664. {
  665. /*
  666.  * Given a filespec p, which may contain any combination of drive, directory,
  667.  * filename, "." or "..", we must derive the complete path name to pass to
  668.  * print.
  669.  */
  670.     int  flags, disk;
  671.     static char newdrive[MAXDRIVE], filname[MAXFILE], filext[MAXEXT];
  672.     static char newdir[MAXDIR], tempdir[MAXDIR], currdir[MAXDIR];
  673.     char *s;
  674.  
  675.     flags = fnsplit(p,newdrive,newdir,filname,filext);
  676.     translit(newdir,'/','\\');
  677.  
  678.     if (flags & DRIVE) {
  679.         /* drive specified, check it out, return(-1) if bad */
  680.         /* getcurdir() does not put a leading "\" so we glue one on        */
  681.         currdir[0] = '\\';
  682.         if (getcurdir(toupper(newdrive[0])-'A'+1,&currdir[1]) != 0)    return(-1);
  683.     } else {
  684.         /* get our default drive, and the current directory */
  685.         disk = getdisk();
  686.         strcpy(newdrive," :");
  687.         newdrive[0] = (char) (disk+'A');
  688.         currdir[0] = '\\';
  689.         getcurdir(disk+1,&currdir[1]);
  690.     }
  691.  
  692.     /* currdir now contains the current directory for the desired drive */
  693.     /* currdir has a leading \ and no trailing \ */
  694.  
  695.     if (flags & DIRECTORY) {
  696.         /* newdir directory specified, but it may be in terms of . and .. */
  697.         /* build a new directory in tempdir, then copy it into newdir     */
  698.         s = newdir;
  699.  
  700.         if (newdir[0] == '\\') {
  701.             /* directory begins at root */
  702.             tempdir[0] = '\0';
  703.         } else {
  704.             /* directory begins with current directory */
  705.             strcpy(tempdir,currdir);
  706.             strcat(tempdir,"\\");
  707.         }
  708.  
  709.         while (*s) {
  710.             if (strncmp(s,".\\",2) == 0) {
  711.                 s += 2;
  712.             } else if (strncmp(s,"..\\",3) == 0) {
  713.                 parent(tempdir);
  714.                 s += 3;
  715.             } else {
  716.                 s = catdir(tempdir,s);
  717.             }
  718.         }
  719.         strcpy(newdir,tempdir);
  720.     } else {
  721.         /* no directory specified, use the default one for the given drive */
  722.         strcpy(newdir,currdir);
  723.     }
  724.     /* all done, merge drive and directory into a full path */
  725.     fnmerge(pathname,newdrive,newdir,filname,filext);
  726.     return(0);
  727. }
  728.  
  729. char *parent(dir)
  730. char *dir;
  731. {
  732. /*  Modify dir by truncating the last directory, thereby giving the parent */
  733. /*  The string will always end in "\" */
  734.  
  735.     char *s ;
  736.     if ((s = strrchr(dir,'\\')) == NULL) {
  737.         /* no directory separators at all */
  738.         strcpy(dir,"\\");
  739.     } else {
  740.         if (*endchar(dir) == '\\') *(endchar(dir)) = '\0';
  741.         if ((s = strrchr(dir,'\\')) == NULL)
  742.             strcpy(dir,"\\");
  743.         else
  744.             *(++s) = '\0';
  745.     }
  746.     return(dir);
  747. }
  748.  
  749. char *catdir(d,s)
  750. char *d, *s;
  751. {
  752. /*
  753.  * Copy a directory from s onto the end of d, include any leading \ and
  754.  * exclude any trailing \
  755.  * Returns a pointer to first character of next directory (or null)
  756.  */
  757.     while (*d) d++;
  758.     if (*s == '\\') *d++ = *s++;
  759.     while (*s != '\\' && *s != '\0') *d++ = *s++;
  760.     *d = '\0';
  761.     return(s);
  762. }
  763.  
  764. char  *endchar(s)
  765. char *s;
  766. {
  767. /* return a pointer to the last character in s */
  768.     if (*s == NULL) return(s);
  769.     while (*s) s++;
  770.     return(--s);
  771. }
  772.  
  773. char *translit(s,c1,c2)
  774. char *s, c1, c2;
  775. {
  776. /*  turn all c1 into c2 in string s, in situ. Return pointer to start of s */
  777.     char *p;
  778.     p = s;
  779.     while (*s) {
  780.         if (*s == c1) *s = c2;
  781.         s++;
  782.     }
  783.     return(p);
  784. }