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