plptools
Loading...
Searching...
No Matches
sisinstaller.cpp
Go to the documentation of this file.
1/*
2 * This file is part of plptools.
3 *
4 * Copyright (C) 2002 Daniel Brahneborg <basic@chello.se>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * along with this program; if not, see <https://www.gnu.org/licenses/>.
18 *
19 */
20
21#include "config.h"
22
23#include "sisinstaller.h"
24
25#include "drive.h"
26#include "plpdirent.h"
27#include "psion.h"
28#include "sisfile.h"
29#include "sisfilelink.h"
30#include "sisfilerecord.h"
31#include "sisreqrecord.h"
32
33#include <cstdlib>
34#include <errno.h>
35#include <unistd.h>
36#include <stdio.h>
37#include <stdbool.h>
38#include <sys/types.h>
39
40#include "ignore-value.h"
41
42static int continueRunning;
43
44static int
45checkAbortHash(void *, uint32_t)
46{
48 {
49 if (logLevel >= 1)
50 {
51 printf("#");
52 fflush(stdout);
53 }
54 }
55 return continueRunning;
56}
57
59{
60 m_installed = nullptr;
61 m_ownInstalled = false;
62}
63
65{
67 {
69 while (curr)
70 {
71 delete curr->m_file;
72 SISFileLink* next = curr->m_next;
73 delete curr;
74 curr = next;
75 }
76 }
77}
78
79void
81{
82 char* end = filename + strlen(filename);
83 while (--end > filename)
84 {
85 char ch = *end;
86 if ((ch == '/') || (ch == '\\'))
87 {
88 *end = 0;
89 if (logLevel >= 1)
90 fprintf(stderr, "Checking for existance of %s\n", filename);
91// if (!m_psion->dirExists(filename))
92 {
93 if (logLevel >= 1)
94 fprintf(stderr, "Creating dir %s\n", filename);
96 res = m_psion->mkdir(filename);
97 if ((res != RFSV::E_PSI_GEN_NONE) &&
99 {
100 fprintf(stderr, " -> Failed: %s\n", (const char*)res);
101 }
102 }
103 *end = ch;
104 return;
105 }
106 }
107}
108
109void
111{
112 uint8_t* destptr = fileRecord->getDestPtr();
113 if (destptr == nullptr)
114 return;
115 if (destptr[0] == '!')
116 {
117 if (m_drive == 0)
118 selectDrive();
120 }
121 int len = fileRecord->m_destLength;
122 char dest[256];
123 memcpy(dest, destptr, len);
124 dest[len] = 0;
125 if (dest[0] == '!')
126 {
127 dest[0] = m_drive;
128 fileRecord->setMainDrive(m_drive);
129 if (logLevel >= 2)
130 fprintf(stderr, "Setting main drive to %c\n", m_drive);
131 }
132
133 char msgbuf[1024];
134 sprintf(msgbuf,
135 "Copying %u bytes to %s\n",
136 fileRecord->m_fileLengths[m_fileNo], dest);
137 {
138 printf("%s\n", msgbuf);
139 }
140
141 copyBuf(fileRecord->getFilePtr(m_fileNo),
142 fileRecord->m_fileLengths[m_fileNo],
143 dest);
144
145}
146
147void
148SISInstaller::copyBuf(const uint8_t* buf, int len, char* name)
149{
150 createDirs(name);
151 char srcName[32];
152 strcpy(srcName, "/tmp/plptools-sis-XXXXXX");
153 int fd = mkstemp(srcName);
154 if (-1 == fd)
155 {
156 fprintf(stderr,
157 "Couldn't create temp file: %s\n", strerror(errno));
158 return;
159 }
161 if (logLevel >= 2)
162 fprintf(stderr, "Storing in %s\n", srcName);
163 ssize_t written = write(fd, buf, len);
164 int close_res = close(fd);
165 bool write_ok = written == len && close_res == 0;
166 if (write_ok) {
167 continueRunning = 1;
168 res = m_psion->copyToPsion(srcName, name, NULL, checkAbortHash);
169 }
170 if (write_ok && res == RFSV::E_PSI_GEN_NONE)
171 {
172 if (logLevel >= 1)
173 fprintf(stderr, " -> Success.\n");
174 }
175 else
176 {
177 fprintf(stderr, " -> Fail: %s\n", (const char*)res);
178 }
179 unlink(srcName);
180}
181
182int
184{
185 char readbuf[8];
186 switch (fileRecord->m_fileType)
187 {
188 case 0:
189 copyFile(fileRecord);
190 break;
191 case 1:
192
193 {
194 printf("Info:\n%.*s\n",
195 (int) fileRecord->m_fileLengths[m_fileNo],
196 fileRecord->getFilePtr(m_fileNo));
197 switch (fileRecord->m_fileDetails)
198 {
199 case 0:
200 printf("Continue\n");
201 ignore_value(fgets(readbuf, sizeof(readbuf), stdin));
202 break;
203 case 1: {
204 printf("(Install next file?) [Y]es/No\n");
205 char *res = fgets(readbuf, sizeof(readbuf), stdin);
206 if (!res || strchr("Nn", readbuf[0]))
207 {
208 return FILE_SKIP;
209 }
210 break;
211 }
212 case 2: {
213 printf("(Continue installation?) [Y]es/No\n");
214 char *res = fgets(readbuf, sizeof(readbuf), stdin);
215 if (!res || strchr("Nn", readbuf[0]))
216 {
217 // Watch out if we have copied any files
218 // already.
219 //
220 return FILE_ABORT;
221 }
222 break;
223 }
224 }
225 }
226 break;
227 case 2:
228 {
229 if (logLevel >= 1)
230 fprintf(stderr, "Recursive sis file...\n");
231 SISFile sisFile;
232 uint8_t* buf2 = fileRecord->getFilePtr(m_fileNo);
233 off_t len = fileRecord->m_fileLengths[m_fileNo];
234// if (m_lastSisFile < fileptr + len)
235// m_lastSisFile = fileptr + len;
236 SisRC rc = sisFile.fillFrom(buf2, len);
237 if (rc != SIS_OK)
238 {
239 fprintf(stderr,
240 "Could not read contained sis file, rc = %d\n", rc);
241 break;
242 }
243 SISInstaller installer;
244 installer.setPsion(m_psion);
245 installer.setInstalled(m_installed);
246 rc = installer.run(&sisFile, buf2, len, m_file);
247 if (0 == m_drive)
248 {
251 if (logLevel >= 1)
252 fprintf(stderr,
253 "Updated drive to %c from recursive sis file\n",
254 m_drive);
255 }
256 break;
257 }
258 case 3:
259 if (logLevel >= 1)
260 fprintf(stderr, "Run %.*s during installation/remove\n",
261 (int) fileRecord->m_destLength, fileRecord->getDestPtr());
262 break;
263 case 4:
264 if (logLevel >= 2)
265 fprintf(stderr, "Running the app will create %.*s\n",
266 (int) fileRecord->m_destLength, fileRecord->getDestPtr());
267 break;
268 }
269 return FILE_OK;
270}
271
272#define SYSTEMINSTALL "c:\\system\\install\\"
273
274SisRC
276{
277 PlpDir files;
279
280 if ((res = m_psion->dir(SYSTEMINSTALL, files)) != RFSV::E_PSI_GEN_NONE)
281 {
282 return SIS_FAILED;
283 }
284 else
285 {
286 while (!files.empty())
287 {
288 PlpDirent file = files[0];
289 if (logLevel >= 1)
290 fprintf(stderr, "Loading sis file `%s'\n", file.getName());
291 char sisname[256];
292 sprintf(sisname, "%s%s", SYSTEMINSTALL, file.getName());
293 loadPsionSis(sisname);
294 files.pop_front();
295 }
296 return SIS_OK;
297 }
298}
299
300void
302{
303 char srcName[32];
304 strcpy(srcName, "/tmp/plptools-sis-XXXXXX");
305 int fd = mkstemp(srcName);
306 if (-1 == fd)
307 {
308 fprintf(stderr, "Couldn't create temp file: %s\n", strerror(errno));
309 return;
310 }
312 continueRunning = 1;
313 if (logLevel >= 2)
314 fprintf(stderr, "Copying from %s to temp file %s\n", name, srcName);
315 res = m_psion->copyFromPsion(name, fd, checkAbortHash);
316 if (res == RFSV::E_PSI_GEN_NONE)
317 {
318 off_t fileLen = lseek(fd, 0, SEEK_END);
319 if (logLevel >= 2)
320 fprintf(stderr, "Read %d bytes from the Psion file %s\n",
321 (int)fileLen, name);
322 lseek(fd, SEEK_SET, 0);
323 uint8_t* sisbuf = new uint8_t[fileLen];
324 int rc = read(fd, sisbuf, fileLen);
325 if (rc == fileLen)
326 {
327 SISFile* sisFile = new SISFile();
328 SisRC rc2 = sisFile->fillFrom(sisbuf, fileLen);
329 if (rc2 == SIS_OK)
330 {
331 if (logLevel >= 1)
332 fprintf(stderr, " Ok.\n");
333 SISFileLink* link = new SISFileLink(sisFile);
334 link->m_next = m_installed;
335 m_ownInstalled = true;
336 m_installed = link;
337 sisFile->ownBuffer();
338 }
339 else
340 {
341 delete sisFile;
342 delete[] sisbuf;
343 }
344 }
345 }
346 close(fd);
347 unlink(srcName);
348}
349
350void
352{
353 int len = fileRecord->m_destLength;
354 char dest[256];
355 memcpy(dest, fileRecord->getDestPtr(), len);
356 dest[len] = 0;
357 if (logLevel >= 1)
358 fprintf(stderr, "Removing file component %s.\n", dest);
359 m_psion->remove(dest);
360}
361
362SisRC
363SISInstaller::run(SISFile* file, uint8_t* buf, off_t len)
364{
365 return run(file, buf, len, 0);
366}
367
368SisRC
369SISInstaller::run(SISFile* file, uint8_t* buf, off_t len, SISFile* parent)
370{
371 int n;
372 long lang;
373 m_file = file;
374 m_buf = buf;
375 char msgbuf[1024];
376 if (parent == 0)
377 {
379 if (n == 1)
380 {
381 sprintf(msgbuf,
382 _("You have only one language: %s"),
383 m_file->getLanguage(0)->m_name);
384 printf("%s\n", msgbuf);
385 lang = 0;
386 }
387 else
388 {
389 printf("Select a language (%d alternatives):\n", n);
390 for (int i = 0; i < n; ++i)
391 printf(" %d. %s\n", i, m_file->getLanguage(i)->m_name);
392 lang = 0;
393 }
394 }
395 else
396 {
397 // This needs to check the _name_ of the language, since the
398 // recursive sis file might have a different language list.
399 // For now, defalt to 0.
400 // lang = parent->getLanguage();
401 lang = 0;
402 if (logLevel >= 1)
403 fprintf(stderr, "Forcing language to %ld\n", lang);
404 }
405 m_file->setLanguage(lang);
406 uint8_t* compName = m_file->getName();
407 sprintf(msgbuf, _("Installing component: `%s'"), compName);
408 printf("%s\n", msgbuf);
409
410 // In order to check requisites and previous versions, we need to
411 // load all sis files from the c:/system/install directory.
412 // This is the only way to find out if a specific application or
413 // library has been loaded, since the sis file names could be just
414 // about anything.
415 //
416 if (m_installed == 0)
418
419 // Check Requisites.
420 //
422 if (logLevel >= 1)
423 fprintf(stderr, "Found %d requisites, of some sort.\n", n);
424 for (int i = 0; i < n; ++i)
425 {
426 SISReqRecord* reqRecord = &m_file->m_reqRecords[i];
427 if (logLevel >= 1)
428 fprintf(stderr,
429 " Check if app with uid %08x exists with version >= %d.%d\n",
430 reqRecord->m_uid, reqRecord->m_major, reqRecord->m_minor);
431 }
432
433 // Check previous version.
434 //
435 if (logLevel >= 1)
436 fprintf(stderr,
437 "Checking if this app (uid %08x) exists with a version less than %d.%d.\n",
441
442 bool uninstallFirst = false;
443 SISFileLink* curr = m_installed;
444 SISFile* oldFile = 0;
445 while (curr)
446 {
447 SISFile* sisFile = curr->m_file;
448 switch (sisFile->compareApp(m_file))
449 {
450 case SIS_VER_EARLIER:
451 uninstallFirst = true;
452 oldFile = sisFile;
453 break;
454
456 // Ask for confirmation.
457 uninstallFirst = true;
458 oldFile = sisFile;
459 break;
460
462 // Ask for confirmation.
463 uninstallFirst = true;
464 oldFile = sisFile;
465 break;
466 }
467 curr = curr->m_next;
468 }
469
470 if (uninstallFirst)
471 {
472// printf("You should uninstall the previous version first.\n");
473// if (!m_forced)
474// return SIS_ABORTED;
475// printf("Forced mode... Installing anyway!\n");
476 if (oldFile == 0)
477 fprintf(stderr, "Already installed, but 0?\n");
478 else
479 {
480 sprintf(msgbuf, "%s", _("Uninstalling the previous version first."));
481 printf("%s\n", msgbuf);
482 uninstall(oldFile);
483 }
484 }
485
486 // Install file components.
487 //
489 if (logLevel >= 1)
490 fprintf(stderr, "Found %d files.\n", n);
491 m_drive = (parent == 0) ? 0 : parent->m_header.m_installationDrive;
492 int nCopiedFiles = 0;
493 m_lastSisFile = 0;
494 bool skipnext = false;
495 bool aborted = false;
496 while (!aborted && (n-- > 0))
497 {
498 SISFileRecord* fileRecord = &m_file->m_fileRecords[n];
499 m_fileNo = (fileRecord->m_flags & 1) ? lang : 0;
500
501 if (skipnext)
502 {
503 skipnext = false;
504 }
505 else
506 {
507 switch (installFile(fileRecord))
508 {
509 case FILE_OK:
510 break;
511 case FILE_SKIP:
512 skipnext = true;
513 break;
514 case FILE_ABORT:
515 aborted = true;
516 break;
517 }
518 }
519 if (!aborted)
520 nCopiedFiles++;
521 }
522 m_file->setFiles(nCopiedFiles);
523 if (logLevel >= 1)
524 fprintf(stderr,
525 "Installed %d files of %d, cutting at offset %u.\n",
529 if (nCopiedFiles == 0)
530 {
531 // There is no need to copy any uninstall information to the
532 // psion, unless we've actually copied anything there.
533 //
534 return SIS_ABORTED;
535 }
536
537 // Copy the updated sis file to the epoc machine.
538 //
539 char resname[256];
540 int namelen = 0;
541 while (compName[namelen] != 0)
542 {
543 if (compName[namelen] == ' ')
544 break;
545 namelen++;
546 }
547 sprintf(resname, "C:\\System\\Install\\%.*s.sis", namelen, compName);
548 if (logLevel >= 1)
549 fprintf(stderr, "Creating residual sis file %s\n", resname);
550 copyBuf(buf, m_file->getResidualEnd(), resname);
551 if (aborted)
552 return SIS_ABORTED;
553 return SIS_OK;
554}
555
556void
558{
559 uint32_t devbits = 0;
561 char drivelist[26];
562 int ndrives = 0;
563 if ((res = m_psion->devlist(devbits)) == RFSV::E_PSI_GEN_NONE)
564 {
565 for (int i = 0; i < 26; i++)
566 {
567 Drive drive;
568 if (((devbits & 1) != 0) &&
569 (m_psion->devinfo(i + 'A', drive) == RFSV::E_PSI_GEN_NONE))
570 {
571 MediaType mediaType = drive.getMediaType();
572 if ((mediaType == MediaType::kDisk) || (mediaType == MediaType::kRAM))
573 {
574 drivelist[ndrives] = 'A' + i;
575 printf("%c: %lud bytes free, %lud bytes total\n",
576 'A' + i,
577 drive.getSpace(),
578 drive.getSize());
579 ++ndrives;
580 }
581 }
582 devbits >>= 1;
583 }
584 }
585 drivelist[ndrives] = 0;
586 if (ndrives == 0)
587 {
588 m_drive = 'C';
589 if (logLevel >= 1)
590 printf("Selecting the only drive %c\n", m_drive);
591 }
592 else if (ndrives == 1)
593 {
594 m_drive = drivelist[0];
595 if (logLevel >= 1)
596 printf("Selecting the only drive %c\n", m_drive);
597 }
598 else
599 {
600
601 {
602 printf("Please select a drive\n");
603 char ch;
604 char readbuf[2];
605 while (m_drive == 0)
606 {
607 if (fgets(readbuf, 2, stdin) == NULL)
608 {
609 printf("please enter a drive");
610 continue;
611 }
612 ch = readbuf[0];
613 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
614 {
615 m_drive = toupper(ch);
616 if (!strchr(drivelist, m_drive))
617 {
618 m_drive = 0;
619 printf("Please select a valid drive: %s\n", drivelist);
620 }
621 }
622 }
623 }
624
625 }
626}
627
628void
630{
631 m_psion = psion;
632}
633
634void
636{
637 int n = file->m_header.m_nfiles;
638 int fileix = n - file->m_header.m_installationFiles;
639 if (logLevel >= 1)
640 fprintf(stderr, "Uninstalling %d files, from a total of %d.\n",
642 file->m_header.m_nfiles);
643 int lang = file->getLanguage();
644 while (fileix < n)
645 {
646 SISFileRecord* fileRecord = &file->m_fileRecords[fileix];
647 m_fileNo = (fileRecord->m_flags & 1) ? lang : 0;
648 char drive = file->m_header.m_installationDrive;
649 fileRecord->setMainDrive(drive);
650 uninstallFile(fileRecord);
651 ++fileix;
652 }
653}
654
655void
657{
658 switch (fileRecord->m_fileType)
659 {
660 case 0:
661 case 4:
662 removeFile(fileRecord);
663 break;
664 case 2:
665 {
666#if 0
667 // This is messy... We can't remove the sis component unless
668 // we've stored the entire component in the residual sis
669 // file on the target machine.
670 if (logLevel >= 1)
671 fprintf(stderr, "Recursive sis file...\n");
672 SISFile sisFile;
673 int fileptr = fileRecord->m_filePtrs[m_fileNo];
674 uint8_t* buf2 = m_buf + fileptr;
675 off_t len = fileRecord->m_fileLengths[m_fileNo];
676 if (m_lastSisFile < fileptr + len)
677 m_lastSisFile = fileptr + len;
678 SisRC rc = sisFile.fillFrom(buf2, len);
679 if (rc != SIS_OK)
680 {
681 fprintf(stderr,
682 "Could not read contained sis file, rc = %d\n", rc);
683 break;
684 }
685 SISInstaller installer;
686 installer.setPsion(m_psion);
687 installer.setInstalled(m_installed);
688 rc = installer.run(&sisFile, buf2, len, m_file);
689 if (0 == m_drive)
690 {
693 if (logLevel >= 1)
694 fprintf(stderr,
695 "Updated drive to %c from recursive sis file\n",
696 m_drive);
697 }
698#endif
699 break;
700 }
701 }
702}
A class representing information about a Disk drive on the psion.
Definition drive.h:51
MediaType getMediaType() const
Retrieve the media type of the drive.
Definition drive.cc:82
uint64_t getSpace() const
Retrieve the free capacity on the drive.
Definition drive.cc:102
uint64_t getSize() const
Retrieve the total capacity of the drive.
Definition drive.cc:98
Wrapper class featuring range-checking and string representation of enumerated values.
Definition Enum.h:135
A class, representing a directory entry of the Psion.
Definition plpdirent.h:79
const char * getName() const
Retrieve the file name of a directory entry.
Definition plpdirent.cc:102
Semi smart proxy for communicating with a Psion.
Definition psion.h:33
virtual Enum< RFSV::errs > copyToPsion(const char *const from, const char *const to, void *, cpCallback_t func)
Definition psion.cpp:61
virtual Enum< RFSV::errs > dir(const char *dir, PlpDir &files)
Definition psion.cpp:75
virtual Enum< RFSV::errs > copyFromPsion(const char *const from, int fd, cpCallback_t func)
Definition psion.cpp:57
virtual Enum< RFSV::errs > devlist(uint32_t &devbits)
Definition psion.cpp:69
virtual Enum< RFSV::errs > devinfo(const char drive, Drive &plpDrive)
Definition psion.cpp:65
virtual void remove(const char *name)
Definition psion.cpp:106
virtual Enum< RFSV::errs > mkdir(const char *dir)
Definition psion.cpp:102
@ E_PSI_FILE_EXIST
Definition rfsv.h:138
@ E_PSI_GEN_NONE
Definition rfsv.h:114
uint32_t m_uid1
uint16_t m_nlangs
uint16_t m_major
uint16_t m_nreqs
uint16_t m_minor
uint16_t m_installationFiles
uint16_t m_nfiles
uint32_t m_installationDrive
Information about a file component in a SIS file.
uint8_t * getFilePtr(int fileNo)
Return a pointer to the file data for the file for the specified language.
uint32_t m_flags
1 if multiple language versions, otherwise 0.
uint32_t m_fileDetails
If file type is 1:
uint32_t m_destLength
uint32_t m_fileType
Type of file.
void setMainDrive(char drive)
uint8_t * getDestPtr()
uint32_t * m_filePtrs
uint32_t * m_fileLengths
The top level container of a SIS file.
Definition sisfile.h:34
void setDrive(char drive)
Set the installed drive.
Definition sisfile.cpp:155
SisRC fillFrom(uint8_t *buf, off_t len)
Populate the fields.
Definition sisfile.cpp:48
SISReqRecord * m_reqRecords
Definition sisfile.h:109
void setFiles(int nFiles)
Set the number of installed files.
Definition sisfile.cpp:161
SisRC compareApp(SISFile *other)
Compare uid and version number of this file, with another.
Definition sisfile.cpp:42
SISFileRecord * m_fileRecords
Definition sisfile.h:108
void ownBuffer()
Definition sisfile.h:81
SISFileHeader m_header
Definition sisfile.h:106
void setLanguage(int lang)
Set the selected installation language.
Definition sisfile.cpp:167
int getLanguage()
Return the currently selected installation language.
Definition sisfile.cpp:137
uint32_t getResidualEnd()
Get the number of bytes that should be copied to the residual sis file on the psion.
Definition sisfile.h:76
uint8_t * getName()
Get the name of this component, in the selected language.
Definition sisfile.cpp:149
A minimal SIS installer.
void selectDrive()
Ask the user which drive to install to.
void loadPsionSis(const char *name)
SISFileLink * m_installed
void removeFile(SISFileRecord *fileRecord)
virtual ~SISInstaller()
void copyBuf(const uint8_t *buf, int len, char *name)
Store the contents of a buffer in a file on the Psion.
bool m_ownInstalled
void setPsion(Psion *psion)
Set the Psion manager.
SISFile * m_file
Psion * m_psion
void uninstallFile(SISFileRecord *fileRecord)
int installFile(SISFileRecord *fileRecord)
void createDirs(char *filename)
SisRC run(SISFile *file, uint8_t *buf, off_t len)
void copyFile(SISFileRecord *fileRecord)
Copy a file to the Psion.
void uninstall(SISFile *sisFile)
uint8_t * m_buf
SisRC loadInstalled()
void setInstalled(SISFileLink *installed)
Set the base pointer to the list of already installed applications, so we don't have to scan it for e...
Information about an application that must be installed prior to the current one.
uint32_t m_uid
uint16_t m_major
uint16_t m_minor
MediaType
Definition drive.h:31
static int checkAbortHash(void *, uint32_t)
Definition ftp.cc:169
static int continueRunning
Definition ftp.cc:88
#define _(String)
Definition plpintl.h:34
std::deque< class PlpDirent > PlpDir
Definition rfsv.h:34
static int checkAbortHash(void *, uint32_t)
#define SYSTEMINSTALL
static int continueRunning
int logLevel
Definition sistypes.cpp:25
SisRC
Return Codes.
Definition sistypes.h:27
@ SIS_OK
Definition sistypes.h:28
@ SIS_ABORTED
Definition sistypes.h:33
@ SIS_VER_EARLIER
Definition sistypes.h:35
@ SIS_FAILED
Definition sistypes.h:32
@ SIS_SAME_OR_LATER
Definition sistypes.h:36
@ SIS_OTHER_VARIANT
Definition sistypes.h:37