1 /* work.c
2    Routines to read command files.
3 
4    Copyright (C) 1991, 1992, 1993, 1995, 2002 Ian Lance Taylor
5 
6    This file is part of the Taylor UUCP package.
7 
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2 of the
11    License, or (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
21 
22    The author of the program may be contacted at ian@airs.com.
23    */
24 
25 #include "uucp.h"
26 
27 #if USE_RCS_ID
28 const char work_rcsid[] = "$FreeBSD$";
29 #endif
30 
31 #include "uudefs.h"
32 #include "uuconf.h"
33 #include "system.h"
34 #include "sysdep.h"
35 
36 #include <ctype.h>
37 #include <errno.h>
38 
39 #if HAVE_OPENDIR
40 #if HAVE_DIRENT_H
41 #include <dirent.h>
42 #else /* ! HAVE_DIRENT_H */
43 #include <sys/dir.h>
44 #define dirent direct
45 #endif /* ! HAVE_DIRENT_H */
46 #endif /* HAVE_OPENDIR */
47 
48 /* Local functions.  */
49 
50 static char *zswork_directory P((const char *zsystem));
51 static boolean fswork_file P((const char *zsystem, const char *zfile,
52 			      char *pbgrade));
53 static int iswork_cmp P((constpointer pkey, constpointer pdatum));
54 
55 /* These functions can support multiple actions going on at once.
56    This allows the UUCP package to send and receive multiple files at
57    the same time.  */
58 
59 /* The ssfilename structure holds the name of a work file, as well as
60    its grade.  */
61 
62 struct ssfilename
63 {
64   char *zfile;
65   char bgrade;
66   /* Some compiler may need this, and it won't normally hurt.  */
67   char bdummy;
68 };
69 
70 /* The ssfile structure holds a command file name and all the lines
71    read in from that command file.  The union within the ssline
72    structure initially holds a line from the file and then holds a
73    pointer back to the ssfile structure; a pointer to this union is
74    used as a sequence pointer.  The ztemp entry of the ssline
75    structure holds the name of a temporary file to delete, if any.  */
76 
77 #define CFILELINES (10)
78 
79 struct ssline
80 {
81   char *zline;
82   struct ssfile *qfile;
83   char *ztemp;
84 };
85 
86 struct ssfile
87 {
88   char *zfile;
89   char bgrade;
90   /* bdummy is needed for some buggy compilers.  */
91   char bdummy;
92   int clines;
93   int cdid;
94   struct ssline aslines[CFILELINES];
95 };
96 
97 /* Static variables for the work scan.  */
98 
99 static struct ssfilename *asSwork_files;
100 static size_t cSwork_files;
101 static size_t iSwork_file;
102 static struct ssfile *qSwork_file;
103 
104 /* Given a system name, return a directory to search for work.  */
105 
106 static char *
zswork_directory(zsystem)107 zswork_directory (zsystem)
108      const char *zsystem;
109 {
110 #if SPOOLDIR_V2
111   return zbufcpy (".");
112 #endif /* SPOOLDIR_V2 */
113 #if SPOOLDIR_BSD42 || SPOOLDIR_BSD43
114   return zbufcpy ("C.");
115 #endif /* SPOOLDIR_BSD42 || SPOOLDIR_BSD43 */
116 #if SPOOLDIR_HDB || SPOOLDIR_SVR4
117   return zbufcpy (zsystem);
118 #endif /* SPOOLDIR_HDB || SPOOLDIR_SVR4 */
119 #if SPOOLDIR_ULTRIX
120   return zsappend3 ("sys",
121 		    (fsultrix_has_spool (zsystem)
122 		     ? zsystem
123 		     : "DEFAULT"),
124 		    "C.");
125 #endif /* SPOOLDIR_ULTRIX */
126 #if SPOOLDIR_TAYLOR
127   return zsysdep_in_dir (zsystem, "C.");
128 #endif /* SPOOLDIR_TAYLOR */
129 }
130 
131 /* See whether a file name from the directory returned by
132    zswork_directory is really a command for a particular system.
133    Return the command grade.  */
134 
135 /*ARGSUSED*/
136 static boolean
fswork_file(zsystem,zfile,pbgrade)137 fswork_file (zsystem, zfile, pbgrade)
138      const char *zsystem ATTRIBUTE_UNUSED;
139      const char *zfile;
140      char *pbgrade;
141 {
142 #if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 || SPOOLDIR_ULTRIX
143   int cfilesys, csys;
144 
145   /* The file name should be C.ssssssgqqqq, where g is exactly one
146      letter and qqqq is exactly four numbers.  The system name may be
147      truncated to six or seven characters.  The system name of the
148      file must match the system name we're looking for, since there
149      could be work files for several systems in one directory.  */
150   if (zfile[0] != 'C' || zfile[1] != '.')
151     return FALSE;
152   csys = strlen (zsystem);
153   cfilesys = strlen (zfile) - 7;
154   if (csys != cfilesys
155       && (csys < 6 || (cfilesys != 6 && cfilesys != 7)))
156     return FALSE;
157   *pbgrade = zfile[cfilesys + 2];
158   return strncmp (zfile + 2, zsystem, cfilesys) == 0;
159 #endif /* V2 || BSD42 || BSD43 || ULTRIX */
160 #if SPOOLDIR_HDB || SPOOLDIR_SVR4
161   int clen;
162 
163   /* The HDB file name should be C.ssssssgqqqq where g is exactly one
164      letter and qqqq is exactly four numbers or letters.  We don't
165      check the system name, because it is guaranteed by the directory
166      we are looking in and some versions of uucp set it to the local
167      system rather than the remote one.  I'm not sure of the exact
168      format of the SVR4 file name, but it does not include the grade
169      at all.  */
170   if (zfile[0] != 'C' || zfile[1] != '.')
171     return FALSE;
172   clen = strlen (zfile);
173   if (clen < 7)
174     return FALSE;
175 #if ! SPOOLDIR_SVR4
176   *pbgrade = zfile[clen - 5];
177 #endif
178   return TRUE;
179 #endif /* SPOOLDIR_HDB || SPOOLDIR_SVR4 */
180 #if SPOOLDIR_TAYLOR
181   /* We don't keep the system name in the file name, since that
182      forces truncation.  Our file names are always C.gqqqq.  */
183   *pbgrade = zfile[2];
184   return (zfile[0] == 'C'
185 	  && zfile[1] == '.'
186 	  && zfile[2] != '\0');
187 #endif /* SPOOLDIR_TAYLOR */
188 }
189 
190 /* A comparison function to look through the list of file names.  */
191 
192 static int
iswork_cmp(pkey,pdatum)193 iswork_cmp (pkey, pdatum)
194      constpointer pkey;
195      constpointer pdatum;
196 {
197   const struct ssfilename *qkey = (const struct ssfilename *) pkey;
198   const struct ssfilename *qdatum = (const struct ssfilename *) pdatum;
199 
200   return strcmp (qkey->zfile, qdatum->zfile);
201 }
202 
203 /* See whether there is any work to do for a particular system.  */
204 
205 boolean
fsysdep_has_work(qsys)206 fsysdep_has_work (qsys)
207      const struct uuconf_system *qsys;
208 {
209   char *zdir;
210   DIR *qdir;
211   struct dirent *qentry;
212 #if SPOOLDIR_SVR4
213   DIR *qgdir;
214   struct dirent *qgentry;
215 #endif
216 
217   zdir = zswork_directory (qsys->uuconf_zname);
218   if (zdir == NULL)
219     return FALSE;
220   qdir = opendir ((char *) zdir);
221   if (qdir == NULL)
222     {
223       ubuffree (zdir);
224       return FALSE;
225     }
226 
227 #if SPOOLDIR_SVR4
228   qgdir = qdir;
229   while ((qgentry = readdir (qgdir)) != NULL)
230     {
231       char *zsub;
232 
233       if (qgentry->d_name[0] == '.'
234 	  || qgentry->d_name[1] != '\0')
235 	continue;
236       zsub = zsysdep_in_dir (zdir, qgentry->d_name);
237       qdir = opendir (zsub);
238       ubuffree (zsub);
239       if (qdir == NULL)
240 	continue;
241 #endif
242 
243       while ((qentry = readdir (qdir)) != NULL)
244 	{
245 	  char bgrade;
246 
247 	  if (fswork_file (qsys->uuconf_zname, qentry->d_name, &bgrade))
248 	    {
249 	      closedir (qdir);
250 #if SPOOLDIR_SVR4
251 	      closedir (qgdir);
252 #endif
253 	      ubuffree (zdir);
254 	      return TRUE;
255 	    }
256 	}
257 
258 #if SPOOLDIR_SVR4
259       closedir (qdir);
260     }
261   qdir = qgdir;
262 #endif
263 
264   closedir (qdir);
265   ubuffree (zdir);
266   return FALSE;
267 }
268 
269 /* Initialize the work scan.  We have to read all the files in the
270    work directory, so that we can sort them by work grade.  The bgrade
271    argument is the minimum grade to consider.  We don't want to return
272    files that we have already considered; usysdep_get_work_free will
273    clear the data out when we are done with the system.  This returns
274    FALSE on error.  */
275 
276 #define CWORKFILES (10)
277 
278 boolean
fsysdep_get_work_init(qsys,bgrade,cmax)279 fsysdep_get_work_init (qsys, bgrade, cmax)
280      const struct uuconf_system *qsys;
281      int bgrade;
282      unsigned int cmax;
283 {
284   char *zdir;
285   DIR *qdir;
286   struct dirent *qentry;
287   size_t chad;
288   size_t callocated;
289 #if SPOOLDIR_SVR4
290   DIR *qgdir;
291   struct dirent *qgentry;
292 #endif
293 
294   zdir = zswork_directory (qsys->uuconf_zname);
295   if (zdir == NULL)
296     return FALSE;
297 
298   qdir = opendir (zdir);
299   if (qdir == NULL)
300     {
301       boolean fret;
302 
303       if (errno == ENOENT)
304 	fret = TRUE;
305       else
306 	{
307 	  ulog (LOG_ERROR, "opendir (%s): %s", zdir, strerror (errno));
308 	  fret = FALSE;
309 	}
310       ubuffree (zdir);
311       return fret;
312     }
313 
314   chad = cSwork_files;
315   callocated = cSwork_files;
316 
317   /* Sort the files we already know about so that we can check the new
318      ones with bsearch.  It would be faster to use a hash table, and
319      the code should be probably be changed.  The sort done at the end
320      of this function does not suffice because it only includes the
321      files added last time, and does not sort the entire array.  Some
322      (bad) qsort implementations are very slow when given a sorted
323      array, which causes particularly bad effects here.  */
324   if (chad > 0)
325     qsort ((pointer) asSwork_files, chad, sizeof (struct ssfilename),
326 	   iswork_cmp);
327 
328 #if SPOOLDIR_SVR4
329   qgdir = qdir;
330   while ((qgentry = readdir (qgdir)) != NULL)
331     {
332       char *zsub;
333 
334       if (qgentry->d_name[0] == '.'
335 	  || qgentry->d_name[1] != '\0'
336 	  || UUCONF_GRADE_CMP (bgrade, qgentry->d_name[0]) < 0)
337 	continue;
338       zsub = zsysdep_in_dir (zdir, qgentry->d_name);
339       qdir = opendir (zsub);
340       if (qdir == NULL)
341 	{
342 	  if (errno != ENOTDIR && errno != ENOENT)
343 	    {
344 	      ulog (LOG_ERROR, "opendir (%s): %s", zsub,
345 		    strerror (errno));
346 	      ubuffree (zsub);
347 	      return FALSE;
348 	    }
349 	  ubuffree (zsub);
350 	  continue;
351 	}
352       ubuffree (zsub);
353 #endif
354 
355       while ((qentry = readdir (qdir)) != NULL)
356 	{
357 	  char bfilegrade;
358 	  char *zname;
359 	  struct ssfilename slook;
360 
361 #if ! SPOOLDIR_SVR4
362 	  zname = zbufcpy (qentry->d_name);
363 #else
364 	  zname = zsysdep_in_dir (qgentry->d_name, qentry->d_name);
365 	  bfilegrade = qgentry->d_name[0];
366 #endif
367 
368 	  slook.zfile = zname;
369 	  if (! fswork_file (qsys->uuconf_zname, qentry->d_name,
370 			     &bfilegrade)
371 	      || UUCONF_GRADE_CMP (bgrade, bfilegrade) < 0
372 	      || (asSwork_files != NULL
373 		  && bsearch ((pointer) &slook,
374 			      (pointer) asSwork_files,
375 			      chad, sizeof (struct ssfilename),
376 			      iswork_cmp) != NULL))
377 	    ubuffree (zname);
378 	  else
379 	    {
380 	      DEBUG_MESSAGE1 (DEBUG_SPOOLDIR,
381 			      "fsysdep_get_work_init: Found %s",
382 			      zname);
383 
384 	      if (cSwork_files >= callocated)
385 		{
386 		  callocated += CWORKFILES;
387 		  asSwork_files =
388 		    ((struct ssfilename *)
389 		     xrealloc ((pointer) asSwork_files,
390 			       (callocated * sizeof (struct ssfilename))));
391 		}
392 
393 	      asSwork_files[cSwork_files].zfile = zname;
394 	      asSwork_files[cSwork_files].bgrade = bfilegrade;
395 	      ++cSwork_files;
396 	      if (cmax != 0 && cSwork_files - chad > cmax)
397 		break;
398 	    }
399 	}
400 
401 #if SPOOLDIR_SVR4
402       closedir (qdir);
403       if (cmax != 0 && cSwork_files - chad > cmax)
404 	break;
405     }
406   qdir = qgdir;
407 #endif
408 
409   closedir (qdir);
410   ubuffree (zdir);
411 
412   /* Sorting the files alphabetically will get the grades in the
413      right order, since all the file prefixes are the same.  */
414   if (cSwork_files > iSwork_file)
415     qsort ((pointer) (asSwork_files + iSwork_file),
416 	   cSwork_files - iSwork_file,
417 	   sizeof (struct ssfilename), iswork_cmp);
418 
419   return TRUE;
420 }
421 
422 /* Get the next work entry for a system.  This must parse the next
423    line in the next work file.  The type of command is set into
424    qcmd->bcmd If there are no more commands, qcmd->bcmd is set to 'H'.
425    Each field in the structure is set to point to a spot in an
426    malloced string.  The grade argument is never used; it has been
427    used by fsysdep_get_work_init.  */
428 
429 /*ARGSUSED*/
430 boolean
fsysdep_get_work(qsys,bgrade,cmax,qcmd)431 fsysdep_get_work (qsys, bgrade, cmax, qcmd)
432      const struct uuconf_system *qsys;
433      int bgrade ATTRIBUTE_UNUSED;
434      unsigned int cmax ATTRIBUTE_UNUSED;
435      struct scmd *qcmd;
436 {
437   char *zdir;
438 
439   if (qSwork_file != NULL && qSwork_file->cdid >= qSwork_file->clines)
440     qSwork_file = NULL;
441 
442   if (asSwork_files == NULL)
443     {
444       qcmd->bcmd = 'H';
445       return TRUE;
446     }
447 
448   zdir = NULL;
449 
450   /* This loop continues until a line is returned.  */
451   while (TRUE)
452     {
453       /* This loop continues until a file is opened and read in.  */
454       while (qSwork_file == NULL)
455 	{
456 	  FILE *e;
457 	  struct ssfile *qfile;
458 	  int iline, callocated;
459 	  char *zline;
460 	  size_t cline;
461 	  char *zname;
462 	  char bfilegrade;
463 
464 	  /* Read all the lines of a command file into memory.  */
465 	  do
466 	    {
467 	      if (iSwork_file >= cSwork_files)
468 		{
469 		  qcmd->bcmd = 'H';
470 		  ubuffree (zdir);
471 		  return TRUE;
472 		}
473 
474 	      if (zdir == NULL)
475 		{
476 		  zdir = zswork_directory (qsys->uuconf_zname);
477 		  if (zdir == NULL)
478 		    return FALSE;
479 		}
480 
481 	      zname = zsysdep_in_dir (zdir, asSwork_files[iSwork_file].zfile);
482 	      bfilegrade = asSwork_files[iSwork_file].bgrade;
483 
484 	      ++iSwork_file;
485 
486 	      e = fopen (zname, "r");
487 	      if (e == NULL)
488 		{
489 		  ulog (LOG_ERROR, "fopen (%s): %s", zname,
490 			strerror (errno));
491 		  ubuffree (zname);
492 		}
493 	    }
494 	  while (e == NULL);
495 
496 	  qfile = (struct ssfile *) xmalloc (sizeof (struct ssfile));
497 	  callocated = CFILELINES;
498 	  iline = 0;
499 
500 	  zline = NULL;
501 	  cline = 0;
502 	  while (getline (&zline, &cline, e) > 0)
503 	    {
504 	      if (iline >= callocated)
505 		{
506 		  /* The sizeof (struct ssfile) includes CFILELINES
507 		     entries already, so using callocated * sizeof
508 		     (struct ssline) will give us callocated *
509 		     CFILELINES entries.  */
510 		  qfile =
511 		    ((struct ssfile *)
512 		     xrealloc ((pointer) qfile,
513 			       (sizeof (struct ssfile) +
514 				(callocated * sizeof (struct ssline)))));
515 		  callocated += CFILELINES;
516 		}
517 	      qfile->aslines[iline].zline = zbufcpy (zline);
518 	      qfile->aslines[iline].qfile = NULL;
519 	      qfile->aslines[iline].ztemp = NULL;
520 	      iline++;
521 	    }
522 
523 	  xfree ((pointer) zline);
524 
525 	  if (fclose (e) != 0)
526 	    ulog (LOG_ERROR, "fclose: %s", strerror (errno));
527 
528 	  if (iline == 0)
529 	    {
530 	      /* There were no lines in the file; this is a poll file,
531 		 for which we return a 'P' command.  */
532 	      qfile->aslines[0].zline = zbufcpy ("P");
533 	      qfile->aslines[0].qfile = NULL;
534 	      qfile->aslines[0].ztemp = NULL;
535 	      iline = 1;
536 	    }
537 
538 	  qfile->zfile = zname;
539 	  qfile->bgrade = bfilegrade;
540 	  qfile->clines = iline;
541 	  qfile->cdid = 0;
542 	  qSwork_file = qfile;
543 	}
544 
545       /* This loop continues until all the lines from the current file
546 	 are used up, or a line is returned.  */
547       while (TRUE)
548 	{
549 	  int iline;
550 
551 	  if (qSwork_file->cdid >= qSwork_file->clines)
552 	    {
553 	      /* We don't want to free qSwork_file here, since it must
554 		 remain until all the lines have been completed.  It
555 		 is freed in fsysdep_did_work.  */
556 	      qSwork_file = NULL;
557 	      /* Go back to the main loop which finds another file.  */
558 	      break;
559 	    }
560 
561 	  iline = qSwork_file->cdid;
562 	  ++qSwork_file->cdid;
563 
564 	  /* Now parse the line into a command.  */
565 	  if (! fparse_cmd (qSwork_file->aslines[iline].zline, qcmd))
566 	    {
567 	      ulog (LOG_ERROR, "Bad line in command file %s",
568 		    qSwork_file->zfile);
569 	      ubuffree (qSwork_file->aslines[iline].zline);
570 	      qSwork_file->aslines[iline].zline = NULL;
571 	      continue;
572 	    }
573 	  qcmd->bgrade = qSwork_file->bgrade;
574 
575 	  qSwork_file->aslines[iline].qfile = qSwork_file;
576 	  qcmd->pseq = (pointer) (&qSwork_file->aslines[iline]);
577 
578 	  if (qcmd->bcmd == 'S' || qcmd->bcmd == 'E')
579 	    {
580 	      char *zreal;
581 
582 	      zreal = zsysdep_spool_file_name (qsys, qcmd->ztemp,
583 					       qcmd->pseq);
584 	      if (zreal == NULL)
585 		{
586 		  ubuffree (qSwork_file->aslines[iline].zline);
587 		  qSwork_file->aslines[iline].zline = NULL;
588 		  ubuffree (zdir);
589 		  return FALSE;
590 		}
591 	      qSwork_file->aslines[iline].ztemp = zreal;
592 	    }
593 
594 	  ubuffree (zdir);
595 	  return TRUE;
596 	}
597     }
598 }
599 
600 /* When a command has been complete, fsysdep_did_work is called.  The
601    sequence entry was set above to be the address of an aslines
602    structure whose pfile entry points to the ssfile corresponding to
603    this file.  We can then check whether all the lines have been
604    completed (they will have been if the pfile entry is NULL) and
605    remove the file if they have been.  This means that we only remove
606    a command file if we manage to complete every transfer it specifies
607    in a single UUCP session.  I don't know if this is how regular UUCP
608    works.  */
609 
610 boolean
fsysdep_did_work(pseq)611 fsysdep_did_work (pseq)
612      pointer pseq;
613 {
614   struct ssfile *qfile;
615   struct ssline *qline;
616   int i;
617 
618   qline = (struct ssline *) pseq;
619 
620   ubuffree (qline->zline);
621   qline->zline = NULL;
622 
623   qfile = qline->qfile;
624   qline->qfile = NULL;
625 
626   /* Remove the temporary file, if there is one.  It really doesn't
627      matter if this fails, and not checking the return value lets us
628      attempt to remove D.0 or whatever an unused temporary file is
629      called without complaining.  */
630   if (qline->ztemp != NULL)
631     {
632       (void) remove (qline->ztemp);
633       ubuffree (qline->ztemp);
634       qline->ztemp = NULL;
635     }
636 
637   /* If not all the lines have been returned from fsysdep_get_work,
638      we can't remove the file yet.  */
639   if (qfile->cdid < qfile->clines)
640     return TRUE;
641 
642   /* See whether all the commands have been completed.  */
643   for (i = 0; i < qfile->clines; i++)
644     if (qfile->aslines[i].qfile != NULL)
645       return TRUE;
646 
647   /* All commands have finished.  */
648   if (remove (qfile->zfile) != 0)
649     {
650       ulog (LOG_ERROR, "remove (%s): %s", qfile->zfile,
651 	    strerror (errno));
652       return FALSE;
653     }
654 
655   ubuffree (qfile->zfile);
656   xfree ((pointer) qfile);
657 
658   if (qfile == qSwork_file)
659     qSwork_file = NULL;
660 
661   return TRUE;
662 }
663 
664 /* Free up the results of a work scan, when we're done with this
665    system.  */
666 
667 /*ARGSUSED*/
668 void
usysdep_get_work_free(qsys)669 usysdep_get_work_free (qsys)
670      const struct uuconf_system *qsys ATTRIBUTE_UNUSED;
671 {
672   if (asSwork_files != NULL)
673     {
674       size_t i;
675 
676       for (i = 0; i < cSwork_files; i++)
677 	ubuffree ((pointer) asSwork_files[i].zfile);
678       xfree ((pointer) asSwork_files);
679       asSwork_files = NULL;
680       cSwork_files = 0;
681       iSwork_file = 0;
682     }
683   if (qSwork_file != NULL)
684     {
685       int i;
686 
687       ubuffree (qSwork_file->zfile);
688       for (i = 0; i < qSwork_file->cdid; i++)
689 	{
690 	  ubuffree (qSwork_file->aslines[i].zline);
691 	  ubuffree (qSwork_file->aslines[i].ztemp);
692 	}
693       for (i = qSwork_file->cdid; i < qSwork_file->clines; i++)
694 	ubuffree (qSwork_file->aslines[i].zline);
695       xfree ((pointer) qSwork_file);
696       qSwork_file = NULL;
697     }
698 }
699 
700 /* Save the temporary file used by a send command, and return an
701    informative message to mail to the requestor.  This is called when
702    a file transfer failed, to make sure that the potentially valuable
703    file is not completely lost.  */
704 
705 const char *
zsysdep_save_temp_file(pseq)706 zsysdep_save_temp_file (pseq)
707      pointer pseq;
708 {
709   struct ssline *qline = (struct ssline *) pseq;
710   char *zto, *zslash;
711   size_t cwant;
712   static char *zbuf;
713   static size_t cbuf;
714 
715   if (! fsysdep_file_exists (qline->ztemp))
716     return NULL;
717 
718   zslash = strrchr (qline->ztemp, '/');
719   if (zslash == NULL)
720     zslash = qline->ztemp;
721   else
722     ++zslash;
723 
724   zto = zbufalc (sizeof PRESERVEDIR + sizeof "/" + strlen (zslash));
725   sprintf (zto, "%s/%s", PRESERVEDIR, zslash);
726 
727   if (! fsysdep_move_file (qline->ztemp, zto, TRUE, FALSE, FALSE,
728 			   (const char *) NULL))
729     {
730       /* Leave the file where it was, not that is much help.  */
731       ubuffree (zto);
732       return "Could not move file to preservation directory";
733     }
734 
735   cwant = sizeof "File saved as\n\t/" + strlen (zSspooldir) + strlen (zto);
736   if (cwant > cbuf)
737     {
738       ubuffree (zbuf);
739       zbuf = zbufalc (cwant);
740       cbuf = cwant;
741     }
742 
743   sprintf (zbuf, "File saved as\n\t%s/%s", zSspooldir, zto);
744   ubuffree (zto);
745   return zbuf;
746 }
747 
748 /* Get the jobid of a work file.  This is needed by uustat.  */
749 
750 char *
zsysdep_jobid(qsys,pseq)751 zsysdep_jobid (qsys, pseq)
752      const struct uuconf_system *qsys;
753      pointer pseq;
754 {
755   return zsfile_to_jobid (qsys, ((struct ssline *) pseq)->qfile->zfile,
756 			  bsgrade (pseq));
757 }
758 
759 /* Get the grade of a work file.  The pseq argument can be NULL when
760    this is called from zsysdep_spool_file_name, and simply means that
761    this is a remote file; returning -1 will cause zsfind_file to do
762    the right thing.  */
763 
764 int
bsgrade(pseq)765 bsgrade (pseq)
766      pointer pseq;
767 {
768   const char *zfile;
769   char bgrade;
770 
771   if (pseq == NULL)
772     return -1;
773 
774   zfile = ((struct ssline *) pseq)->qfile->zfile;
775 
776 #if SPOOLDIR_TAYLOR
777   bgrade = *(strrchr (zfile, '/') + 3);
778 #else
779 #if ! SPOOLDIR_SVR4
780   bgrade = zfile[strlen (zfile) - CSEQLEN - 1];
781 #else
782   bgrade = *(strchr (zfile, '/') + 1);
783 #endif
784 #endif
785 
786   return bgrade;
787 }
788