Subversion Repositories SvarDOS

Rev

Rev 280 | Rev 310 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
28 mv_fox 1
/*
190 mateuszvis 2
 * SVARDOS INSTALL PROGRAM
206 mateuszvis 3
 * Copyright (C) 2021 Mateusz Viste
4
 *
190 mateuszvis 5
 * PUBLISHED UNDER THE TERMS OF THE MIT LICENSE
42 mv_fox 6
 *
190 mateuszvis 7
 * COPYRIGHT (C) 2016-2021 MATEUSZ VISTE, ALL RIGHTS RESERVED.
94 mv_fox 8
 *
190 mateuszvis 9
 * Permission is hereby granted, free of charge, to any person obtaining a
10
 * copy of this software and associated documentation files (the "Software"),
11
 * to deal in the Software without restriction, including without limitation
12
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13
 * and/or sell copies of the Software, and to permit persons to whom the
14
 * Software is furnished to do so, subject to the following conditions:
94 mv_fox 15
 *
190 mateuszvis 16
 * The above copyright notice and this permission notice shall be included in
17
 * all copies or substantial portions of the Software.
94 mv_fox 18
 *
190 mateuszvis 19
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25
 * DEALINGS IN THE SOFTWARE.
94 mv_fox 26
 *
190 mateuszvis 27
 * http://svardos.osdn.io
28 mv_fox 28
 */
29
 
30
#include <dos.h>
30 mv_fox 31
#include <direct.h>  /* mkdir() */
28 mv_fox 32
#include <stdio.h>   /* printf() and friends */
33
#include <stdlib.h>  /* system() */
34
#include <string.h>  /* memcpy() */
35
#include <unistd.h>
42 mv_fox 36
 
37
#include "kitten\kitten.h"
38
 
53 mv_fox 39
#include "cdrom.h"
28 mv_fox 40
#include "input.h"
41
#include "video.h"
42
 
67 mv_fox 43
/* keyboard layouts and locales */
44
#include "keylay.h"
45
#include "keyoff.h"
42 mv_fox 46
 
29 mv_fox 47
/* color scheme (color, mono) */
48
static unsigned short COLOR_TITLEBAR[2] = {0x7000,0x7000};
49
static unsigned short COLOR_BODY[2] = {0x1700,0x0700};
50
static unsigned short COLOR_SELECT[2] = {0x7000,0x7000};
51
static unsigned short COLOR_SELECTCUR[2] = {0x1F00,0x0700};
28 mv_fox 52
 
29 mv_fox 53
/* mono flag */
54
static int mono = 0;
28 mv_fox 55
 
190 mateuszvis 56
/* how much disk space does SvarDOS require (in MiB) */
57
#define SVARDOS_DISK_REQ 8
28 mv_fox 58
 
73 mv_fox 59
/* menu screens can output only one of these: */
60
#define MENUNEXT 0
61
#define MENUPREV -1
62
#define MENUQUIT -2
63
 
67 mv_fox 64
/* a convenience 'function' used for debugging */
65
#define DBG(x) { video_putstringfix(24, 0, 0x4F00u, x, 80); }
47 mv_fox 66
 
67 mv_fox 67
struct slocales {
68
  char lang[4];
69
  char *keybcode;
70
  unsigned int codepage;
71
  int egafile;
72
  int keybfile;
73
  int keyboff;
74
  int keyblen;
96 mv_fox 75
  unsigned int keybid;
67 mv_fox 76
};
77
 
78
 
28 mv_fox 79
/* reboot the computer */
80
static void reboot(void) {
81
  void ((far *bootroutine)()) = (void (far *)()) 0xFFFF0000L;
82
  int far *rstaddr = (int far *)0x00400072L; /* BIOS boot flag is at 0040:0072 */
83
  *rstaddr = 0x1234; /* 0x1234 = warm boot, 0 = cold boot */
84
  (*bootroutine)(); /* jump to the BIOS reboot routine at FFFF:0000 */
85
}
86
 
42 mv_fox 87
 
56 mv_fox 88
/* outputs a string to screen with taking care of word wrapping. returns amount of lines. */
89
static int putstringwrap(int y, int x, unsigned short attr, char *s) {
90
  int linew, lincount;
91
  linew = 80;
92
  if (x >= 0) linew -= (x << 1);
93
 
94
  for (lincount = 1; y+lincount < 25; lincount++) {
95
    int i, len = linew;
96
    for (i = 0; i <= linew; i++) {
97
      if (s[i] == ' ') len = i;
98
      if (s[i] == '\n') {
99
        len = i;
100
        break;
101
      }
102
      if (s[i] == 0) {
103
        len = i;
104
        break;
105
      }
106
    }
107
    video_putstring(y++, x, attr, s, len);
108
    s += len;
109
    if (*s == 0) break;
110
    s += 1; /* skip the whitespace char */
111
  }
112
  return(lincount);
113
}
114
 
115
 
116
/* an NLS wrapper around video_putstring(), also performs line wrapping when
117
 * needed. returns the amount of lines that were output */
118
static int putstringnls(int y, int x, unsigned short attr, int nlsmaj, int nlsmin, char *s) {
42 mv_fox 119
  s = kittengets(nlsmaj, nlsmin, s);
56 mv_fox 120
  return(putstringwrap(y, x, attr, s));
42 mv_fox 121
}
122
 
123
 
280 mateuszvis 124
/* copy file f1 to f2 using buff as a buffer of buffsz bytes. f2 will be overwritten if it
125
 * exists already! returns 0 on success. */
126
static int fcopy(const char *f2, const char *f1, void *buff, size_t buffsz) {
127
  FILE *fd1, *fd2;
128
  size_t sz;
129
  int res = -1; /* assume failure */
130
 
131
  /* open files */
132
  fd1 = fopen(f1, "rb");
133
  fd2 = fopen(f2, "wb");
134
  if ((fd1 == NULL) || (fd2 == NULL)) goto QUIT;
135
 
136
  /* copy data */
137
  for (;;) {
138
    sz = fread(buff, 1, buffsz, fd1);
139
    if (sz == 0) {
140
      if (feof(fd1) != 0) break;
141
      goto QUIT;
142
    }
143
    if (fwrite(buff, 1, sz, fd2) != sz) goto QUIT;
144
  }
145
 
146
  res = 0; /* success */
147
 
148
  QUIT:
149
  if (fd1 != NULL) fclose(fd1);
150
  if (fd2 != NULL) fclose(fd2);
151
  return(res);
152
}
153
 
154
 
67 mv_fox 155
static int menuselect(int ypos, int xpos, int height, char **list, int listlen) {
28 mv_fox 156
  int i, offset = 0, res = 0, count, width = 0;
67 mv_fox 157
  /* count how many positions there is, and check their width */
158
  for (count = 0; (list[count] != NULL) && (count != listlen); count++) {
28 mv_fox 159
    int len = strlen(list[count]);
160
    if (len > width) width = len;
161
  }
162
 
163
  /* if xpos negative, means 'center out' */
164
  if (xpos < 0) xpos = 39 - (width >> 1);
165
 
29 mv_fox 166
  video_putchar(ypos, xpos+width+2, COLOR_SELECT[mono], 0xBF);         /*       \ */
167
  video_putchar(ypos, xpos-1, COLOR_SELECT[mono], 0xDA);               /*  /      */
168
  video_putchar(ypos+height-1, xpos-1, COLOR_SELECT[mono], 0xC0);      /*  \      */
169
  video_putchar(ypos+height-1, xpos+width+2, COLOR_SELECT[mono], 0xD9);/*      /  */
170
  video_putcharmulti(ypos, xpos, COLOR_SELECT[mono], 0xC4, width + 2, 1);
171
  video_putcharmulti(ypos+height-1, xpos, COLOR_SELECT[mono], 0xC4, width + 2, 1);
172
  video_putcharmulti(ypos+1, xpos-1, COLOR_SELECT[mono], 0xB3, height - 2, 80);
173
  video_putcharmulti(ypos+1, xpos+width+2, COLOR_SELECT[mono], 0xB3, height - 2, 80);
28 mv_fox 174
 
175
  for (;;) {
176
    int key;
177
    /* list of selectable items */
178
    for (i = 0; i < height - 2; i++) {
179
      if (i + offset == res) {
29 mv_fox 180
        video_putchar(ypos + 1 + i, xpos, COLOR_SELECTCUR[mono], 16);
181
        video_putchar(ypos + 1 + i, xpos+width+1, COLOR_SELECTCUR[mono], 17);
28 mv_fox 182
        video_movecursor(ypos + 1 + i, xpos);
29 mv_fox 183
        video_putstringfix(ypos + 1 + i, xpos+1, COLOR_SELECTCUR[mono], list[i + offset], width);
28 mv_fox 184
      } else if (i + offset < count) {
29 mv_fox 185
        video_putchar(ypos + 1 + i, xpos, COLOR_SELECT[mono], ' ');
186
        video_putchar(ypos + 1 + i, xpos+width+1, COLOR_SELECT[mono], ' ');
187
        video_putstringfix(ypos + 1 + i, xpos+1, COLOR_SELECT[mono], list[i + offset], width);
28 mv_fox 188
      } else {
29 mv_fox 189
        video_putcharmulti(ypos + 1 + i, xpos, COLOR_SELECT[mono], ' ', width+2, 1);
28 mv_fox 190
      }
191
    }
192
    key = input_getkey();
193
    if (key == 0x0D) { /* ENTER */
194
      return(res);
195
    } else if (key == 0x148) { /* up */
33 mv_fox 196
      if (res > 0) {
197
        res--;
198
        if (res < offset) offset = res;
199
      }
28 mv_fox 200
    } else if (key == 0x150) { /* down */
33 mv_fox 201
      if (res+1 < count) {
202
        res++;
203
        if (res > offset + height - 3) offset = res - (height - 3);
204
      }
205
    } else if (key == 0x147) { /* home */
206
      res = 0;
207
      offset = 0;
208
    } else if (key == 0x14F) { /* end */
209
      res = count - 1;
210
      if (res > offset + height - 3) offset = res - (height - 3);
28 mv_fox 211
    } else if (key == 0x1B) {  /* ESC */
212
      return(-1);
78 mv_fox 213
    }/* else {
33 mv_fox 214
      char buf[8];
55 mv_fox 215
      snprintf(buf, sizeof(buf), "0x%02X ", key);
56 mv_fox 216
      video_putstring(1, 0, COLOR_BODY[mono], buf, -1);
78 mv_fox 217
    }*/
28 mv_fox 218
  }
219
}
220
 
79 mv_fox 221
static void newscreen(int statusbartype) {
222
  char *msg;
190 mateuszvis 223
  msg = kittengets(0, 0, "SVARDOS INSTALLATION");
81 mv_fox 224
  video_putcharmulti(0, 0, COLOR_TITLEBAR[mono], ' ', 80, 1);
79 mv_fox 225
  video_putstring(0, 40 - (strlen(msg) >> 1), COLOR_TITLEBAR[mono], msg, -1);
81 mv_fox 226
  video_clear(COLOR_BODY[mono], 80, -80);
79 mv_fox 227
  switch (statusbartype) {
228
    case 1:
229
      msg = kittengets(0, 11, "Up/Down = Select entry | Enter = Validate your choice | ESC = Quit to DOS");
230
      break;
231
    case 2:
232
      msg = kittengets(0, 5, "Press any key...");
233
      break;
234
    case 3:
235
      msg = "";
236
      break;
237
    default:
238
      msg = kittengets(0, 10, "Up/Down = Select entry | Enter = Validate your choice | ESC = Previous screen");
239
      break;
240
  }
241
  video_putchar(24, 0, COLOR_TITLEBAR[mono], ' ');
242
  video_putstringfix(24, 1, COLOR_TITLEBAR[mono], msg, 79);
36 mv_fox 243
  video_movecursor(25,0);
28 mv_fox 244
}
245
 
96 mv_fox 246
/* fills a slocales struct accordingly to the value of its keyboff member */
247
static void kblay2slocal(struct slocales *locales) {
248
  char *m;
249
  for (m = kblayouts[locales->keyboff]; *m != 0; m++); /* skip layout name */
250
  m++;
251
  /* skip keyb code and copy it to locales.keybcode */
252
  locales->keybcode = m;
253
  for (; *m != 0; m++);
254
  /* */
255
  locales->codepage = ((unsigned short)m[1] << 8) | m[2];
256
  locales->egafile = m[3];
257
  locales->keybfile = m[4];
258
  locales->keybid = ((unsigned short)m[5] << 8) | m[6];
259
}
260
 
67 mv_fox 261
static int selectlang(struct slocales *locales) {
262
  int choice, x;
42 mv_fox 263
  char *msg;
28 mv_fox 264
  char *langlist[] = {
67 mv_fox 265
    "English",
266
    "French",
133 mv_fox 267
    "German",
119 mv_fox 268
    "Italian",
67 mv_fox 269
    "Polish",
116 mv_fox 270
    "Russian",
123 mv_fox 271
    "Slovene",
128 mv_fox 272
    "Swedish",
67 mv_fox 273
    "Turkish",
28 mv_fox 274
    NULL
275
  };
276
 
79 mv_fox 277
  newscreen(1);
190 mateuszvis 278
  msg = kittengets(1, 0, "Welcome to SvarDOS");
42 mv_fox 279
  x = 40 - (strlen(msg) >> 1);
56 mv_fox 280
  video_putstring(4, x, COLOR_BODY[mono], msg, -1);
42 mv_fox 281
  video_putcharmulti(5, x, COLOR_BODY[mono], '=', strlen(msg), 1);
49 mv_fox 282
  putstringnls(8, -1, COLOR_BODY[mono], 1, 1, "Please select your language from the list below:");
133 mv_fox 283
  choice = menuselect(11, -1, 11, langlist, -1);
73 mv_fox 284
  if (choice < 0) return(MENUPREV);
67 mv_fox 285
  /* populate locales with default values */
286
  memset(locales, 0, sizeof(struct slocales));
287
  switch (choice) {
288
    case 1:
289
      strcpy(locales->lang, "FR");
290
      locales->keyboff = OFFLOC_FR;
291
      locales->keyblen = OFFLEN_FR;
292
      break;
293
    case 2:
133 mv_fox 294
      strcpy(locales->lang, "DE");
295
      locales->keyboff = OFFLOC_DE;
296
      locales->keyblen = OFFLEN_DE;
297
      break;
298
    case 3:
119 mv_fox 299
      strcpy(locales->lang, "IT");
300
      locales->keyboff = OFFLOC_IT;
301
      locales->keyblen = OFFLEN_IT;
302
      break;
133 mv_fox 303
    case 4:
67 mv_fox 304
      strcpy(locales->lang, "PL");
305
      locales->keyboff = OFFLOC_PL;
306
      locales->keyblen = OFFLEN_PL;
307
      break;
133 mv_fox 308
    case 5:
116 mv_fox 309
      strcpy(locales->lang, "RU");
310
      locales->keyboff = OFFLOC_RU;
311
      locales->keyblen = OFFLEN_RU;
312
      break;
133 mv_fox 313
    case 6:
123 mv_fox 314
      strcpy(locales->lang, "SI");
315
      locales->keyboff = OFFLOC_SI;
316
      locales->keyblen = OFFLEN_SI;
317
      break;
133 mv_fox 318
    case 7:
128 mv_fox 319
      strcpy(locales->lang, "SV");
320
      locales->keyboff = OFFLOC_SV;
321
      locales->keyblen = OFFLEN_SV;
322
      break;
133 mv_fox 323
    case 8:
67 mv_fox 324
      strcpy(locales->lang, "TR");
325
      locales->keyboff = OFFLOC_TR;
326
      locales->keyblen = OFFLEN_TR;
327
      break;
328
    default:
329
      strcpy(locales->lang, "EN");
330
      locales->keyboff = 0;
331
      locales->keyblen = OFFCOUNT;
332
      break;
333
  }
96 mv_fox 334
  /* populate the slocales struct accordingly to the keyboff member */
335
  kblay2slocal(locales);
67 mv_fox 336
  /* */
73 mv_fox 337
  return(MENUNEXT);
28 mv_fox 338
}
339
 
340
 
67 mv_fox 341
static int selectkeyb(struct slocales *locales) {
96 mv_fox 342
  int menuheight, choice;
343
  if (locales->keyblen == 1) return(MENUNEXT); /* do not ask for keyboard layout if only one is available for given language */
79 mv_fox 344
  newscreen(0);
197 mateuszvis 345
  putstringnls(5, 1, COLOR_BODY[mono], 1, 5, "SvarDOS supports different keyboard layouts. Choose the keyboard layout that you want.");
96 mv_fox 346
  menuheight = locales->keyblen + 2;
67 mv_fox 347
  if (menuheight > 13) menuheight = 13;
96 mv_fox 348
  choice = menuselect(10, -1, menuheight, &(kblayouts[locales->keyboff]), locales->keyblen);
349
  if (choice < 0) return(MENUPREV);
350
  /* (re)load the keyboard layout & codepage setup */
351
  locales->keyboff += choice;
352
  kblay2slocal(locales);
73 mv_fox 353
  return(MENUNEXT);
67 mv_fox 354
}
355
 
356
 
28 mv_fox 357
/* returns 0 if installation must proceed, non-zero otherwise */
358
static int welcomescreen(void) {
73 mv_fox 359
  int c;
190 mateuszvis 360
  char *choice[] = {"Install SvarDOS to disk", "Quit to DOS", NULL};
42 mv_fox 361
  choice[0] = kittengets(0, 1, choice[0]);
362
  choice[1] = kittengets(0, 2, choice[1]);
79 mv_fox 363
  newscreen(0);
190 mateuszvis 364
  putstringnls(4, 1, COLOR_BODY[mono], 2, 0, "You are about to install SvarDOS: a free, MSDOS-compatible operating system based on the FreeDOS kernel. SvarDOS targets 386+ computers and comes with a variety of third-party applications.\n\nWARNING: If your PC has another operating system installed, this other system might be unable to boot once SvarDOS is installed.");
73 mv_fox 365
  c = menuselect(13, -1, 4, choice, -1);
366
  if (c < 0) return(MENUPREV);
367
  if (c == 0) return(MENUNEXT);
368
  return(MENUQUIT);
28 mv_fox 369
}
370
 
371
 
33 mv_fox 372
/* returns 1 if drive is removable, 0 if not, -1 on error */
373
static int isdriveremovable(int drv) {
28 mv_fox 374
  union REGS r;
33 mv_fox 375
  r.x.ax = 0x4408;
376
  r.h.bl = drv;
28 mv_fox 377
  int86(0x21, &r, &r);
33 mv_fox 378
  /* CF set on error, AX set to 0 if removable, 1 if fixed */
379
  if (r.x.cflag != 0) return(-1);
380
  if (r.x.ax == 0) return(1);
381
  return(0);
28 mv_fox 382
}
383
 
384
 
35 mv_fox 385
/* returns total disk space of drive drv (in MiB, max 2048), or -1 if drive invalid */
386
static int disksize(int drv) {
28 mv_fox 387
  long res;
388
  union REGS r;
389
  r.h.ah = 0x36; /* DOS 2+ get free disk space */
390
  r.h.dl = drv;
391
  int86(0x21, &r, &r);
392
  if (r.x.ax == 0xffffu) return(-1); /* AX set to FFFFh if drive invalid */
49 mv_fox 393
  res = r.x.ax;  /* sectors per cluster */
394
  res *= r.x.dx; /* dx contains total clusters, bx contains free clusters */
395
  res *= r.x.cx; /* bytes per sector */
396
  res >>= 20;    /* convert bytes to MiB */
397
  return(res);
35 mv_fox 398
}
399
 
400
 
401
/* returns 0 if disk is empty, non-zero otherwise */
402
static int diskempty(int drv) {
403
  unsigned int rc;
404
  int res;
405
  char buff[8];
406
  struct find_t fileinfo;
55 mv_fox 407
  snprintf(buff, sizeof(buff), "%c:\\*.*", 'A' + drv - 1);
35 mv_fox 408
  rc = _dos_findfirst(buff, _A_NORMAL | _A_SUBDIR | _A_HIDDEN | _A_SYSTEM, &fileinfo);
409
  if (rc == 0) {
410
    res = 1; /* call successfull means disk is not empty */
28 mv_fox 411
  } else {
35 mv_fox 412
    res = 0;
28 mv_fox 413
  }
35 mv_fox 414
  /* _dos_findclose(&fileinfo); */ /* apparently required only on OS/2 */
28 mv_fox 415
  return(res);
416
}
417
 
280 mateuszvis 418
#ifdef DEADCODE
200 mateuszvis 419
/* set new DOS "current drive" to drv ('A', 'B', etc). returns 0 on success */
420
static int set_cur_drive(char drv) {
421
  union REGS r;
422
  if ((drv < 'A') || (drv > 'Z')) return(-1);
423
  r.h.ah = 0x0E; /* DOS 1+ SELECT DEFAULT DRIVE */
424
  r.h.dl = drv - 'A';
425
  int86(0x21, &r, &r);
426
  if (r.h.al < drv - 'A') return(-1);
427
  return(0);
428
}
280 mateuszvis 429
#endif
200 mateuszvis 430
 
431
/* returns 0 if file exists, non-zero otherwise */
432
static int fileexists(const char *fname) {
433
  FILE *fd;
434
  fd = fopen(fname, "rb");
435
  if (fd == NULL) return(-1);
436
  fclose(fd);
437
  return(0);
438
}
439
 
440
 
28 mv_fox 441
static int preparedrive(void) {
33 mv_fox 442
  int driveremovable;
36 mv_fox 443
  int selecteddrive = 3; /* hardcoded to 'C:' for now */
444
  int cselecteddrive;
35 mv_fox 445
  int ds;
73 mv_fox 446
  int choice;
56 mv_fox 447
  char buff[1024];
36 mv_fox 448
  cselecteddrive = 'A' + selecteddrive - 1;
28 mv_fox 449
  for (;;) {
33 mv_fox 450
    driveremovable = isdriveremovable(selecteddrive);
451
    if (driveremovable < 0) {
49 mv_fox 452
      char *list[] = { "Create a partition automatically", "Run the FDISK partitioning tool", "Quit to DOS", NULL};
79 mv_fox 453
      newscreen(0);
42 mv_fox 454
      list[0] = kittengets(0, 3, list[0]);
455
      list[1] = kittengets(0, 4, list[1]);
456
      list[2] = kittengets(0, 2, list[2]);
190 mateuszvis 457
      snprintf(buff, sizeof(buff), kittengets(3, 0, "ERROR: Drive %c: could not be found. Perhaps your hard disk needs to be partitioned first. Please create at least one partition on your hard disk, so SvarDOS can be installed on it. Note, that SvarDOS requires at least %d MiB of available disk space.\n\nYou can use the FDISK partitioning tool for creating the required partition manually, or you can let the installer partitioning your disk automatically. You can also abort the installation to use any other partition manager of your choice."), cselecteddrive, SVARDOS_DISK_REQ);
61 mv_fox 458
      putstringwrap(4, 1, COLOR_BODY[mono], buff);
67 mv_fox 459
      switch (menuselect(14, -1, 5, list, -1)) {
33 mv_fox 460
        case 0:
461
          system("FDISK /AUTO");
462
          break;
463
        case 1:
81 mv_fox 464
          video_clear(0x0700, 0, 0);
33 mv_fox 465
          video_movecursor(0, 0);
466
          system("FDISK");
467
          break;
73 mv_fox 468
        case 2:
469
          return(MENUQUIT);
56 mv_fox 470
        default:
33 mv_fox 471
          return(-1);
472
      }
113 mv_fox 473
      /* write a temporary MBR which only skips the drive (in case BIOS would
474
       * try to boot off the not-yet-ready C: disk) */
475
      system("FDISK /AMBR"); /* writes BOOT.MBR into actual MBR */
79 mv_fox 476
      newscreen(2);
61 mv_fox 477
      putstringnls(10, 10, COLOR_BODY[mono], 3, 1, "Your computer will reboot now.");
42 mv_fox 478
      putstringnls(12, 10, COLOR_BODY[mono], 0, 5, "Press any key...");
33 mv_fox 479
      input_getkey();
28 mv_fox 480
      reboot();
73 mv_fox 481
      return(MENUQUIT);
33 mv_fox 482
    } else if (driveremovable > 0) {
79 mv_fox 483
      newscreen(2);
56 mv_fox 484
      snprintf(buff, sizeof(buff), kittengets(3, 2, "ERROR: Drive %c: is a removable device. Installation aborted."), cselecteddrive);
61 mv_fox 485
      video_putstring(9, 1, COLOR_BODY[mono], buff, -1);
42 mv_fox 486
      putstringnls(11, 2, COLOR_BODY[mono], 0, 5, "Press any key...");
73 mv_fox 487
      return(MENUQUIT);
28 mv_fox 488
    }
33 mv_fox 489
    /* if not formatted, propose to format it right away (try to create a directory) */
55 mv_fox 490
    snprintf(buff, sizeof(buff), "%c:\\SVWRTEST.123", cselecteddrive);
53 mv_fox 491
    if (mkdir(buff) == 0) {
492
      rmdir(buff);
493
    } else {
28 mv_fox 494
      char *list[] = { "Proceed with formatting", "Quit to DOS", NULL};
79 mv_fox 495
      newscreen(0);
42 mv_fox 496
      list[0] = kittengets(0, 6, list[0]);
497
      list[1] = kittengets(0, 2, list[1]);
56 mv_fox 498
      snprintf(buff, sizeof(buff), kittengets(3, 3, "ERROR: Drive %c: seems to be unformated. Do you wish to format it?"), cselecteddrive);
61 mv_fox 499
      video_putstring(7, 1, COLOR_BODY[mono], buff, -1);
73 mv_fox 500
      choice = menuselect(12, -1, 4, list, -1);
501
      if (choice < 0) return(MENUPREV);
502
      if (choice == 1) return(MENUQUIT);
81 mv_fox 503
      video_clear(0x0700, 0, 0);
28 mv_fox 504
      video_movecursor(0, 0);
190 mateuszvis 505
      snprintf(buff, sizeof(buff), "FORMAT %c: /Q /U /Z:seriously /V:SVARDOS", cselecteddrive);
36 mv_fox 506
      system(buff);
28 mv_fox 507
      continue;
508
    }
33 mv_fox 509
    /* check total disk space */
35 mv_fox 510
    ds = disksize(selecteddrive);
190 mateuszvis 511
    if (ds < SVARDOS_DISK_REQ) {
56 mv_fox 512
      int y = 9;
79 mv_fox 513
      newscreen(2);
190 mateuszvis 514
      snprintf(buff, sizeof(buff), kittengets(3, 4, "ERROR: Drive %c: is not big enough! SvarDOS requires a disk of at least %d MiB."), cselecteddrive);
61 mv_fox 515
      y += putstringwrap(y, 1, COLOR_BODY[mono], buff);
73 mv_fox 516
      putstringnls(++y, 1, COLOR_BODY[mono], 0, 5, "Press any key...");
28 mv_fox 517
      input_getkey();
73 mv_fox 518
      return(MENUQUIT);
28 mv_fox 519
    }
520
    /* is the disk empty? */
79 mv_fox 521
    newscreen(0);
35 mv_fox 522
    if (diskempty(selecteddrive) != 0) {
28 mv_fox 523
      char *list[] = { "Proceed with formatting", "Quit to DOS", NULL};
65 mv_fox 524
      int y = 6;
42 mv_fox 525
      list[0] = kittengets(0, 6, list[0]);
526
      list[1] = kittengets(0, 2, list[1]);
190 mateuszvis 527
      snprintf(buff, sizeof(buff), kittengets(3, 5, "ERROR: Drive %c: is not empty. SvarDOS must be installed on an empty disk.\n\nYou can format the disk now, to make it empty. Note however, that this will ERASE ALL CURRENT DATA on your disk."), cselecteddrive);
65 mv_fox 528
      y += putstringwrap(y, 1, COLOR_BODY[mono], buff);
73 mv_fox 529
      choice = menuselect(++y, -1, 4, list, -1);
530
      if (choice < 0) return(MENUPREV);
531
      if (choice == 1) return(MENUQUIT);
81 mv_fox 532
      video_clear(0x0700, 0, 0);
28 mv_fox 533
      video_movecursor(0, 0);
190 mateuszvis 534
      snprintf(buff, sizeof(buff), "FORMAT %c: /Q /U /Z:seriously /V:SVARDOS", cselecteddrive);
42 mv_fox 535
      system(buff);
28 mv_fox 536
      continue;
537
    } else {
538
      /* final confirmation */
190 mateuszvis 539
      char *list[] = { "Install SvarDOS", "Quit to DOS", NULL};
42 mv_fox 540
      list[0] = kittengets(0, 1, list[0]);
541
      list[1] = kittengets(0, 2, list[1]);
190 mateuszvis 542
      snprintf(buff, sizeof(buff), kittengets(3, 6, "The installation of SvarDOS to %c: is about to begin."), cselecteddrive);
56 mv_fox 543
      video_putstring(7, -1, COLOR_BODY[mono], buff, -1);
73 mv_fox 544
      choice = menuselect(10, -1, 4, list, -1);
545
      if (choice < 0) return(MENUPREV);
546
      if (choice == 1) return(MENUQUIT);
62 mv_fox 547
      snprintf(buff, sizeof(buff), "SYS A: %c: > NUL", cselecteddrive);
36 mv_fox 548
      system(buff);
111 mv_fox 549
      system("FDISK /MBR");
55 mv_fox 550
      snprintf(buff, sizeof(buff), "%c:\\TEMP", cselecteddrive);
36 mv_fox 551
      mkdir(buff);
552
      return(cselecteddrive);
28 mv_fox 553
    }
554
  }
555
}
556
 
557
 
280 mateuszvis 558
/* generates locales-related configurations and writes them to file (this
559
 * is used to compute autoexec.bat content) */
560
static void genlocalesconf(FILE *fd, const struct slocales *locales) {
561
  if (locales == NULL) return;
562
 
563
  fprintf(fd, "SET LANG=%s\r\n", locales->lang);
564
 
565
  if (locales->egafile > 0) {
566
    fprintf(fd, "DISPLAY CON=(EGA,,1)\r\n");
567
    if (locales->egafile == 1) {
568
      fprintf(fd, "MODE CON CP PREPARE=((%u) %%DOSDIR%%\\CPI\\EGA.CPX)\r\n", locales->codepage);
569
    } else {
570
      fprintf(fd, "MODE CON CP PREPARE=((%u) %%DOSDIR%%\\CPI\\EGA%d.CPX)\r\n", locales->codepage, locales->egafile);
571
    }
572
    fprintf(fd, "MODE CON CP SELECT=%u\r\n", locales->codepage);
573
  }
574
 
575
  if (locales->keybfile > 0) {
576
    fprintf(fd, "KEYB %s,%d,%%DOSDIR%%\\BIN\\", locales->keybcode, locales->codepage);
577
    if (locales->keybfile == 1) {
578
      fprintf(fd, "KEYBOARD.SYS");
579
    } else {
580
      fprintf(fd, "KEYBRD%d.SYS", locales->keybfile);
581
    }
582
    if (locales->keybid != 0) fprintf(fd, " /ID:%d", locales->keybid);
583
    fprintf(fd, "\r\n");
584
  }
585
}
586
 
587
 
588
static void bootfilesgen(char targetdrv, const struct slocales *locales) {
28 mv_fox 589
  char buff[128];
590
  FILE *fd;
53 mv_fox 591
  /*** CONFIG.SYS ***/
280 mateuszvis 592
  snprintf(buff, sizeof(buff), "%c:\\TEMP\\CONFIG.SYS", targetdrv);
53 mv_fox 593
  fd = fopen(buff, "wb");
594
  if (fd == NULL) return;
101 mv_fox 595
  fprintf(fd, "DOS=UMB,HIGH\r\n"
596
              "LASTDRIVE=Z\r\n"
597
              "FILES=50\r\n");
277 mateuszvis 598
  fprintf(fd, "DEVICE=%c:\\SVARDOS\\BIN\\HIMEMX.EXE\r\n", targetdrv);
77 mv_fox 599
  if (strcmp(locales->lang, "EN") == 0) {
95 mv_fox 600
    strcpy(buff, "COMMAND");
77 mv_fox 601
  } else {
602
    snprintf(buff, sizeof(buff), "CMD-%s", locales->lang);
603
  }
280 mateuszvis 604
  fprintf(fd, "SHELLHIGH=C:\\SVARDOS\\BIN\\%s.COM /E:512 /P\r\n", buff);
605
  fprintf(fd, "REM COUNTRY=001,437,C:\\SVARDOS\\CONF\\COUNTRY.SYS\r\n");
606
  fprintf(fd, "REM DEVICE=C:\\DRIVERS\\UDVD2\\UDVD2.SYS /D:SVCD0001 /H\r\n");
53 mv_fox 607
  fclose(fd);
28 mv_fox 608
  /*** AUTOEXEC.BAT ***/
280 mateuszvis 609
  snprintf(buff, sizeof(buff), "%c:\\TEMP\\AUTOEXEC.BAT", targetdrv);
28 mv_fox 610
  fd = fopen(buff, "wb");
611
  if (fd == NULL) return;
612
  fprintf(fd, "@ECHO OFF\r\n");
280 mateuszvis 613
  fprintf(fd, "SET TEMP=C:\\TEMP\r\n");
614
  fprintf(fd, "SET DOSDIR=C:\\SVARDOS\r\n");
49 mv_fox 615
  fprintf(fd, "SET NLSPATH=%%DOSDIR%%\\NLS\r\n");
53 mv_fox 616
  fprintf(fd, "SET DIRCMD=/OGNE/P/4\r\n");
303 mateuszvis 617
  fprintf(fd, "SET WATTCP.CFG=%%DOSDIR%%\\CFG\r\n");
280 mateuszvis 618
  fprintf(fd, "PATH %%DOSDIR%%\\BIN\r\n");
28 mv_fox 619
  fprintf(fd, "PROMPT $P$G\r\n");
30 mv_fox 620
  fprintf(fd, "ALIAS REBOOT=FDAPM COLDBOOT\r\n");
621
  fprintf(fd, "ALIAS HALT=FDAPM POWEROFF\r\n");
56 mv_fox 622
  fprintf(fd, "FDAPM APMDOS\r\n");
28 mv_fox 623
  fprintf(fd, "\r\n");
280 mateuszvis 624
  genlocalesconf(fd, locales);
49 mv_fox 625
  fprintf(fd, "\r\n");
280 mateuszvis 626
  fprintf(fd, "REM Uncomment the line below for CDROM support\r\n");
627
  fprintf(fd, "REM SHSUCDX /d:SVCD0001\r\n");
628
  fprintf(fd, "\r\n");
49 mv_fox 629
  fprintf(fd, "REM Uncomment the line below for automatic mouse support\r\n");
630
  fprintf(fd, "REM CTMOUSE\r\n");
631
  fprintf(fd, "\r\n");
53 mv_fox 632
  fprintf(fd, "ECHO.\r\n");
190 mateuszvis 633
  fprintf(fd, "ECHO %s\r\n", kittengets(6, 0, "Welcome to SvarDOS! Type 'HELP' if you need help."));
28 mv_fox 634
  fclose(fd);
200 mateuszvis 635
  /*** CREATE DIRECTORY FOR CONFIGURATION FILES ***/
277 mateuszvis 636
  snprintf(buff, sizeof(buff), "%c:\\SVARDOS", targetdrv);
200 mateuszvis 637
  mkdir(buff);
277 mateuszvis 638
  snprintf(buff, sizeof(buff), "%c:\\SVARDOS\\CFG", targetdrv);
53 mv_fox 639
  mkdir(buff);
277 mateuszvis 640
  /*** PKG.CFG ***/
641
  snprintf(buff, sizeof(buff), "%c:\\SVARDOS\\CFG\\PKG.CFG", targetdrv);
642
  fd = fopen(buff, "wb");
643
  if (fd == NULL) return;
644
  fprintf(fd, "# pkg config file - specifies locations where packages should be installed\r\n"
645
              "\r\n"
646
              "# Programs\r\n"
647
              "DIR PROGS C:\\\r\n"
648
              "\r\n"
649
              "# Games \r\n"
650
              "DIR GAMES C:\\\r\n"
651
              "\r\n"
652
              "# Drivers\r\n"
653
              "DIR DRIVERS C:\\DRIVERS\r\n"
654
              "\r\n"
655
              "# Development tools\r\n"
656
              "DIR DEVEL C:\\DEVEL\r\n");
657
  fclose(fd);
53 mv_fox 658
  /*** COUNTRY.SYS ***/
659
  /*** PICOTCP ***/
660
  /*** WATTCP ***/
303 mateuszvis 661
  snprintf(buff, sizeof(buff), "%c:\\SVARDOS\\CFG\\WATTCP.CFG", targetdrv);
662
  fd = fopen(buff, "wb");
663
  if (fd == NULL) return;
664
  fprintf(fd, "my_ip = dhcp\r\n"
665
              "#my_ip = 192.168.0.7\r\n"
666
              "#netmask = 255.255.255.0\r\n"
667
              "#nameserver = 192.168.0.1\r\n"
668
              "#nameserver = 192.168.0.2\r\n"
669
              "#gateway = 192.168.1.1\r\n");
670
  fclose(fd);
28 mv_fox 671
}
672
 
673
 
280 mateuszvis 674
static int installpackages(char targetdrv, char srcdrv, const struct slocales *locales) {
192 mateuszvis 675
  char pkglist[512];
30 mv_fox 676
  int i, pkglistlen;
192 mateuszvis 677
  size_t pkglistflen;
280 mateuszvis 678
  char buff[1024]; /* must be *at least* 1 sector big for efficient file copying */
679
  FILE *fd = NULL;
192 mateuszvis 680
  char *pkgptr;
79 mv_fox 681
  newscreen(3);
192 mateuszvis 682
  /* load pkg list */
683
  fd = fopen("install.lst", "rb");
684
  if (fd == NULL) {
685
    video_putstring(10, 30, COLOR_BODY[mono], "ERROR: INSTALL.LST NOT FOUND", -1);
686
    input_getkey();
687
    return(-1);
688
  }
689
  pkglistflen = fread(pkglist, 1, sizeof(pkglist), fd);
690
  fclose(fd);
691
  if (pkglistflen == sizeof(pkglist)) {
692
    video_putstring(10, 30, COLOR_BODY[mono], "ERROR: INSTALL.LST TOO LARGE", -1);
693
    input_getkey();
694
    return(-1);
695
  }
696
  pkglist[pkglistflen] = 0xff; /* mark the end of list */
697
  /* replace all \r and \n chars by 0 bytes, and count the number of packages */
698
  pkglistlen = 0;
699
  for (i = 0; i < pkglistflen; i++) {
700
    switch (pkglist[i]) {
701
      case '\n':
702
        pkglistlen++;
703
        /* FALLTHRU */
704
      case '\r':
705
        pkglist[i] = 0;
706
        break;
707
    }
708
  }
280 mateuszvis 709
  /* copy pkg.exe to the new drive, along with all packages */
710
  snprintf(buff, sizeof(buff), "%c:\\TEMP\\pkg.exe", targetdrv);
711
  snprintf(buff + 64, sizeof(buff) - 64, "%c:\\pkg.exe", srcdrv);
712
  fcopy(buff, buff + 64, buff, sizeof(buff));
713
 
714
  /* open the post-install autoexec.bat and prepare initial instructions */
715
  snprintf(buff, sizeof(buff), "%c:\\temp\\postinst.bat", targetdrv);
716
  fd = fopen(buff, "wb");
717
  if (fd == NULL) return(-1);
718
  fprintf(fd, "@ECHO OFF\r\n");
719
 
720
  /* copy packages */
192 mateuszvis 721
  pkgptr = pkglist;
722
  for (i = 0;; i++) {
723
    /* move forward to nearest entry or end of list */
724
    while (*pkgptr == 0) pkgptr++;
725
    if (*pkgptr == 0xff) break;
726
    /* install the package */
727
    snprintf(buff, sizeof(buff), kittengets(4, 0, "Installing package %d/%d: %s"), i+1, pkglistlen, pkgptr);
36 mv_fox 728
    strcat(buff, "       ");
192 mateuszvis 729
    video_putstringfix(10, 1, COLOR_BODY[mono], buff, sizeof(buff));
200 mateuszvis 730
    /* wait for new diskette if package not found */
280 mateuszvis 731
    snprintf(buff, sizeof(buff), "%c:\\%s.zip", srcdrv, pkgptr);
200 mateuszvis 732
    while (fileexists(buff) != 0) {
733
      putstringnls(12, 1, COLOR_BODY[mono], 4, 1, "*** INSERT THE DISK THAT CONTAINS THE REQUIRED FILE AND PRESS ANY KEY ***");
734
      input_getkey();
735
      video_putstringfix(12, 1, COLOR_BODY[mono], "", 80); /* erase the 'insert disk' message */
736
    }
280 mateuszvis 737
    /* proceed with package copy (buff contains the src filename already) */
738
    snprintf(buff + 32, sizeof(buff) - 32, "%c:\\temp\\%s.zip", targetdrv, pkgptr);
739
    if (fcopy(buff + 32, buff, buff, sizeof(buff)) != 0) {
740
      video_putstring(10, 30, COLOR_BODY[mono], "READ ERROR", -1);
192 mateuszvis 741
      input_getkey();
280 mateuszvis 742
      fclose(fd);
192 mateuszvis 743
      return(-1);
55 mv_fox 744
    }
280 mateuszvis 745
    /* write install instruction to post-install script */
746
    fprintf(fd, "pkg install %s.zip\r\ndel %s.zip\r\n", pkgptr, pkgptr);
192 mateuszvis 747
    /* jump to next entry or end of list */
748
    while ((*pkgptr != 0) && (*pkgptr != 0xff)) pkgptr++;
749
    if (*pkgptr == 0xff) break;
28 mv_fox 750
  }
280 mateuszvis 751
  /* set up locales so the "installation over" message is nicely displayed */
752
  genlocalesconf(fd, locales);
753
  /* replace autoexec.bat and config.sys now and write some nice message on screen */
754
  fprintf(fd, "DEL pkg.exe\r\n"
755
              "COPY CONFIG.SYS C:\\\r\n"
756
              "DEL CONFIG.SYS\r\n"
757
              "DEL C:\\AUTOEXEC.BAT\r\n"
758
              "COPY AUTOEXEC.BAT C:\\\r\n"
759
              "DEL AUTOEXEC.BAT\r\n");
760
  /* print out the "installation over" message */
761
  fprintf(fd, "ECHO.\r\n"
762
              "ECHO %s\r\n"
763
              "ECHO.\r\n", kittengets(5, 1, "SvarDOS installation is over. Please restart your computer now."));
764
  fclose(fd);
765
 
766
  /* prepare a dummy autoexec.bat that will call temp\postinst.bat */
767
  snprintf(buff, sizeof(buff), "%c:\\autoexec.bat", targetdrv);
768
  fd = fopen(buff, "wb");
769
  if (fd == NULL) return(-1);
770
  fprintf(fd, "@ECHO OFF\r\n"
771
              "SET DOSDIR=C:\\SVARDOS\r\n"
772
              "SET NLSPATH=%%DOSDIR%%\\NLS\r\n"
773
              "PATH %%DOSDIR%%\\BIN\r\n");
774
  fprintf(fd, "CD TEMP\r\n"
775
              "postinst.bat\r\n");
776
  fclose(fd);
777
 
192 mateuszvis 778
  return(0);
28 mv_fox 779
}
780
 
781
 
42 mv_fox 782
static void finalreboot(void) {
56 mv_fox 783
  int y = 9;
79 mv_fox 784
  newscreen(2);
280 mateuszvis 785
  y += putstringnls(y, 1, COLOR_BODY[mono], 5, 0, "Your computer will reboot now.\nPlease remove the installation disk from your drive.");
61 mv_fox 786
  putstringnls(++y, 1, COLOR_BODY[mono], 0, 5, "Press any key...");
42 mv_fox 787
  input_getkey();
788
  reboot();
789
}
790
 
791
 
192 mateuszvis 792
static void loadcp(const struct slocales *locales) {
42 mv_fox 793
  char buff[64];
67 mv_fox 794
  if (locales->codepage == 437) return;
42 mv_fox 795
  video_movecursor(1, 0);
67 mv_fox 796
  if (locales->egafile == 1) {
797
    snprintf(buff, sizeof(buff), "MODE CON CP PREP=((%u) A:\\EGA.CPX) > NUL", locales->codepage);
42 mv_fox 798
  } else {
67 mv_fox 799
    snprintf(buff, sizeof(buff), "MODE CON CP PREP=((%u) A:\\EGA%d.CPX) > NUL", locales->codepage, locales->egafile);
42 mv_fox 800
  }
801
  system(buff);
67 mv_fox 802
  snprintf(buff, sizeof(buff), "MODE CON CP SEL=%u > NUL", locales->codepage);
42 mv_fox 803
  system(buff);
804
  /* below I re-init the video controller - apparently this is required if
65 mv_fox 805
   * I want the new glyph symbols to be actually applied, at least some
806
   * (broken?) BIOSes, like VBox, apply glyphs only at next video mode change */
42 mv_fox 807
  {
808
  union REGS r;
809
  r.h.ah = 0x0F; /* get current video mode */
810
  int86(0x10, &r, &r); /* r.h.al contains the current video mode now */
56 mv_fox 811
  r.h.al |= 128; /* set the high bit of AL to instruct BIOS not to flush VRAM's content (EGA+) */
812
  r.h.ah = 0; /* re-set video mode (to whatever is set in AL) */
42 mv_fox 813
  int86(0x10, &r, &r);
814
  }
815
}
816
 
200 mateuszvis 817
 
193 mateuszvis 818
/* checks that drive drv contains SvarDOS packages
65 mv_fox 819
 * returns 0 if found, non-zero otherwise */
193 mateuszvis 820
static int checkinstsrc(char drv) {
821
  char fname[16];
822
  snprintf(fname, sizeof(fname), "%c:\\ATTRIB.ZIP", drv);
200 mateuszvis 823
  return(fileexists(fname));
69 mv_fox 824
}
65 mv_fox 825
 
826
 
28 mv_fox 827
int main(void) {
67 mv_fox 828
  struct slocales locales;
28 mv_fox 829
  int targetdrv;
193 mateuszvis 830
  int sourcedrv;
73 mv_fox 831
  int action;
28 mv_fox 832
 
193 mateuszvis 833
  /* am I running in install-from-floppy mode? */
834
  if (checkinstsrc('A') == 0) {
835
    sourcedrv = 'A';
836
  } else { /* otherwise find where the cdrom drive is */
837
    sourcedrv = cdrom_findfirst();
838
    if (sourcedrv < 0) {
839
      printf("ERROR: CD-ROM DRIVE NOT FOUND\r\n");
840
      return(1);
841
    }
842
    sourcedrv += 'A'; /* convert the source drive 'id' (A=0) to an actual drive letter */
843
    if (checkinstsrc(sourcedrv) != 0) {
844
      printf("ERROR: SVARDOS INSTALLATION CD NOT FOUND IN THE DRIVE.\r\n");
845
      return(1);
846
    }
53 mv_fox 847
  }
848
 
29 mv_fox 849
  /* init screen and detect mono status */
850
  mono = video_init();
851
 
73 mv_fox 852
  kittenopen("INSTALL"); /* load initial NLS support */
853
 
854
 SelectLang:
190 mateuszvis 855
  action = selectlang(&locales); /* welcome to svardos, select your language */
73 mv_fox 856
  if (action != MENUNEXT) goto Quit;
857
  setenv("LANG", locales.lang, 1);
858
  loadcp(&locales);
859
  kittenclose(); /* reload NLS with new language */
860
  kittenopen("INSTALL"); /* NLS support */
861
  action = selectkeyb(&locales);  /* what keyb layout should we use? */
862
  if (action == MENUQUIT) goto Quit;
863
  if (action == MENUPREV) goto SelectLang;
864
 
865
 WelcomeScreen:
190 mateuszvis 866
  action = welcomescreen(); /* what svardos is, ask whether to run live dos or install */
73 mv_fox 867
  if (action == MENUQUIT) goto Quit;
868
  if (action == MENUPREV) goto SelectLang;
869
  targetdrv = preparedrive(); /* what drive should we install to? check avail. space */
870
  if (targetdrv == MENUQUIT) goto Quit;
871
  if (targetdrv == MENUPREV) goto WelcomeScreen;
280 mateuszvis 872
  bootfilesgen(targetdrv, &locales); /* generate boot files and other configurations */
873
  if (installpackages(targetdrv, sourcedrv, &locales) != 0) goto Quit;    /* install packages */
73 mv_fox 874
  /*localcfg();*/ /* show local params (currency, etc), and propose to change them (based on localcfg) */
875
  /*netcfg();*/ /* basic networking config */
876
  finalreboot(); /* remove the CD and reboot */
877
 
878
 Quit:
42 mv_fox 879
  kittenclose(); /* close NLS support */
81 mv_fox 880
  video_clear(0x0700, 0, 0);
28 mv_fox 881
  video_movecursor(0, 0);
882
  return(0);
883
}