pre-inst/pre-inst.c
author J. Ali Harlow <ali@juiblex.co.uk>
Tue Apr 25 17:41:00 2023 +0100 (2023-04-25)
changeset 109 2947214c450e
parent 99 0121592e2512
permissions -rw-r--r--
Partial fix for #5537
     1 /*
     2  * Copyright (C) 2014, 2020  J. Ali Harlow <ali@juiblex.co.uk>
     3  *
     4  * This program is free software; you can redistribute it and/or modify
     5  * it under the terms of the GNU General Public License as published by
     6  * the Free Software Foundation; either version 2 of the License, or
     7  * (at your option) any later version.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License along
    15  * with this program; if not, write to the Free Software Foundation, Inc.,
    16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    17  *
    18  * References:
    19  *	http://www.transmissionzero.co.uk/computing/win32-apps-with-mingw/
    20  */
    21 
    22 #include "config.h"
    23 #ifndef WIN32
    24 #define _XOPEN_SOURCE 500
    25 #endif
    26 #include <stdlib.h>
    27 #include <stdio.h>
    28 #include <string.h>
    29 #include <errno.h>
    30 #include <lua.h>
    31 #include <razor.h>
    32 #include <plover/plover.h>
    33 #include <plover/uri-handler.h>
    34 #include <whelk/whelk.h>
    35 #ifdef WIN32
    36 #include <windows.h>
    37 #include <process.h>
    38 #include <commctrl.h>
    39 #include "resource.h"
    40 
    41 #ifndef FOF_NO_UI
    42 #define FOF_NO_UI	(FOF_SILENT|FOF_NOCONFIRMATION|FOF_NOERRORUI|\
    43 			FOF_NOCONFIRMMKDIR)
    44 #endif
    45 
    46 #else	/* WIN32 */
    47 #include <ftw.h>
    48 #endif	/* WIN32 */
    49 #include "post.h"
    50 
    51 /*
    52  * Under WIN32, g_spawn may require a helper program which we'd rather avoid.
    53  * At least with glib 2.58, this isn't needed if g_spawn_sync() is used (or
    54  * G_SPAWN_DO_NOT_REAP_CHILD is specified), so we can now define USE_G_SPAWN
    55  * unconditionally.
    56  */
    57 #define USE_G_SPAWN
    58 
    59 LUALIB_API int luaopen_posix(lua_State *L);
    60 
    61 #ifdef WIN32
    62 DWORD main_thread_id;
    63 #endif
    64 
    65 gchar *prefix;
    66 
    67 int verify_and_fix(const char *root)
    68 {
    69     return 0;
    70 }
    71 
    72 #ifdef WIN32
    73 INT_PTR CALLBACK ProgressDialogProc(HWND dialog,UINT msg,WPARAM w_param,
    74   LPARAM l_param)
    75 {
    76     HWND progress;
    77     DWORD style;
    78     switch (msg)
    79     {
    80 	case WM_INITDIALOG:
    81 	    progress=GetDlgItem(dialog,IDC_PROGRESS); 
    82 	    style=GetWindowLong(progress,GWL_STYLE);
    83 	    SetWindowLong(progress,GWL_STYLE,style|PBS_MARQUEE);
    84 	    SendMessage(progress,PBM_SETMARQUEE,(WPARAM)TRUE,(LPARAM)30);
    85 	    return (INT_PTR)TRUE;
    86     }
    87     return (INT_PTR)FALSE;
    88 }
    89 #endif
    90 
    91 #ifdef WIN32
    92 __stdcall
    93 #endif
    94 unsigned pre_install_thread(void *data)
    95 {
    96     gboolean changed;
    97     int retval;
    98     const char *repository=data;
    99     gchar *s,*uri;
   100     char *install[]={"plover-gtkui",NULL};
   101     char **pkgs=install;
   102     struct comps *comps;
   103     struct comps_group *group;
   104     struct comps_requirement *pkg;
   105     struct plover_vector *packages=NULL;
   106     GError *error=NULL;
   107     plover__uri_handler_init();
   108     prefix=plover_pre_install_prefix();
   109     s=g_strconcat(prefix,"/var/log/pre-install",NULL);
   110     plover_log_open(s);
   111     g_free(s);
   112     s=g_strconcat(prefix,"/var/lib/razor",NULL);
   113     uri=razor_path_to_uri(s);
   114     g_free(s);
   115     razor_set_database_uri(uri);
   116     free(uri);
   117     if (verify_and_fix(prefix))
   118     {
   119 	g_free(prefix);
   120 	return -1;
   121     }
   122     s=g_strconcat(repository,"/repodata/comps.xml",NULL);
   123     comps=plover_comps_new_from_uri(s,&error);
   124     retval=!comps;
   125     g_free(s);
   126     if (!retval)
   127     {
   128 	group=plover_comps_lookup_group(comps,"installer");
   129 	if (group)
   130 	{
   131 	    packages=plover_vector_new();
   132 	    do
   133 	    {
   134 		changed=FALSE;
   135 		for(pkg=group->packages;pkg;pkg=pkg->next)
   136 		{
   137 		    if (plover_vector_contains(packages,pkg->name))
   138 			continue;
   139 		    if (pkg->type==COMPS_REQUIREMENT_DEFAULT ||
   140 		      pkg->type==COMPS_REQUIREMENT_MANDATORY ||
   141 		      pkg->type==COMPS_REQUIREMENT_CONDITIONAL &&
   142 		      plover_vector_contains(packages,pkg->requires))
   143 		    {
   144 			changed=TRUE;
   145 			plover_vector_append(packages,pkg->name);
   146 		    }
   147 		}
   148 	    } while(changed);
   149 	    pkgs=packages->strings;
   150 	}
   151     }
   152     if (!retval)
   153 	retval=!plover_install_uri(repository,prefix,pkgs,&error);
   154     if (!retval)
   155 	retval=!plover_update_uri(repository,prefix,NULL,&error);
   156     if (packages)
   157 	plover_vector_free(packages);
   158     if (comps)
   159 	plover_comps_free(comps);
   160     if (error)
   161     {
   162 	fprintf(stderr,"%s\n",error->message);
   163 	g_error_free(error);
   164     }
   165 #ifdef WIN32
   166     PostQuitMessage(retval);
   167     PostThreadMessage(main_thread_id,WM_QUIT,retval,0);
   168     _endthreadex(retval);
   169 #endif
   170     return retval;
   171 }
   172 
   173 /*
   174  * The idea is that if pre_install() fails, update/setup should fall back
   175  * to console interfaces.
   176  */
   177 #ifdef WIN32
   178 HANDLE
   179 #else
   180 void *
   181 #endif
   182 pre_install(const char *repository)
   183 {
   184 #ifdef WIN32
   185     HANDLE retval;
   186 #else
   187     void *retval;
   188 #endif
   189     razor_set_lua_loader("posix",(void (*)())luaopen_posix);
   190     razor_set_lua_loader("whelk",(void (*)())luaopen_whelk);
   191 #ifdef WIN32
   192     retval=(HANDLE)_beginthreadex(NULL,0,pre_install_thread,(void *)repository,
   193       0,NULL);
   194 #else
   195     if (pre_install_thread((void *)repository))
   196 	retval=NULL;
   197     else
   198 	retval=(void *)1;		/* Non-NULL to indicate success */
   199 #endif
   200     return retval;
   201 }
   202 
   203 #ifndef WIN32
   204 int remove_ignore(const char *fpath,const struct stat *sb,int typeflag,
   205   struct FTW *ftwbuf)
   206 {
   207     (void)remove(fpath);
   208     return 0;
   209 }
   210 #endif
   211 
   212 gboolean deltree(const char *path)
   213 {
   214 #ifdef WIN32
   215     /* Based on g_local_file_trash() */
   216     SHFILEOPSTRUCTW op={0};
   217     gboolean success;
   218     wchar_t *wfilename;
   219     long len;
   220     wfilename=g_utf8_to_utf16(path,-1,NULL,&len,NULL);
   221     /* SHFILEOPSTRUCT.pFrom is double-zero-terminated */
   222     wfilename=g_renew(wchar_t,wfilename,len+2);
   223     wfilename[len+1]=0;
   224     op.wFunc=FO_DELETE;
   225     op.pFrom=wfilename;
   226     op.fFlags=FOF_NO_UI;
   227     success=!SHFileOperationW(&op);
   228     if (success && op.fAnyOperationsAborted)
   229 	success=FALSE;
   230     g_free(wfilename);
   231     return success;
   232 #else
   233     return nftw(path,remove_ignore,64,FTW_DEPTH|FTW_PHYS);
   234 #endif
   235 }
   236 
   237 gboolean pre_uninstall(void)
   238 {
   239     gboolean success;
   240     gchar *s,*uri;
   241     GError *error=NULL;
   242     razor_set_lua_loader("posix",(void (*)())luaopen_posix);
   243     razor_set_lua_loader("whelk",(void (*)())luaopen_whelk);
   244     prefix=plover_pre_install_prefix();
   245     s=g_strconcat(prefix,"/var/lib/razor",NULL);
   246     uri=razor_path_to_uri(s);
   247     g_free(s);
   248     razor_set_database_uri(uri);
   249     free(uri);
   250     success=plover_remove(NULL,&error);
   251     if (error)
   252     {
   253 	fprintf(stderr,"%s\n",error->message);
   254 	g_error_free(error);
   255     }
   256     deltree(prefix);
   257     return success;
   258 }
   259 
   260 #if defined(WIN32) && !defined(USE_G_SPAWN)
   261 /* Based on glib's g_spawn_win32.c */
   262 
   263 static gchar *
   264 win32_cmdline_quote(const char *string)
   265 {
   266     const gchar *p=string;
   267     gchar *retval,*q;
   268     gint len=0;
   269     gboolean need_dblquotes=FALSE;
   270     while (*p)
   271     {
   272 	if (*p==' ' || *p=='\t')
   273 	    need_dblquotes=TRUE;
   274 	else if (*p=='"')
   275 	    len++;
   276 	else if (*p=='\\')
   277 	{
   278 	    const gchar *pp=p;
   279 	    while (*pp && *pp=='\\')
   280 		pp++;
   281 	    if (*pp=='"')
   282 		len++;
   283 	}
   284 	len++;
   285 	p++;
   286     }
   287     q=retval=g_malloc(len+need_dblquotes*2+1);
   288     p=string;
   289     if (need_dblquotes)
   290 	*q++='"';
   291     while (*p)
   292     {
   293 	if (*p=='"')
   294 	    *q++='\\';
   295 	else if (*p=='\\')
   296 	{
   297 	    const gchar *pp=p;
   298 	    while (*pp && *pp=='\\')
   299 		pp++;
   300 	    if (*pp=='"')
   301 		*q++='\\';
   302 	}
   303 	*q++=*p;
   304 	p++;
   305     }
   306     if (need_dblquotes)
   307 	*q++='"';
   308     *q++='\0';
   309     return retval;
   310 }
   311 
   312 /* Create a win32-style wide-character argv with suitable quoting */
   313 wchar_t **win32_argv_import(char **argv,GError **error)
   314 {
   315     int i,n;
   316     gchar *s;
   317     wchar_t **wargv;
   318     GError *tmp_error=NULL;
   319     n=g_strv_length(argv);
   320     wargv=g_new(wchar_t *,n+1);
   321     for(i=0;i<n;i++)
   322     {
   323 	s=win32_cmdline_quote(argv[i]);
   324 	wargv[i]=g_utf8_to_utf16(s,-1,NULL,NULL,&tmp_error);
   325 	g_free(s);
   326 	if (!wargv[i])
   327 	{
   328 	    g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
   329 	      "Invalid argument #%d: %s",i,tmp_error->message);
   330 	    g_error_free(tmp_error);
   331 	    for(i--;i>=0;i--)
   332 		g_free(wargv[i]);
   333 	    g_free(wargv);
   334 	    return FALSE;
   335 	}
   336     }
   337     wargv[i]=NULL;
   338     return wargv;
   339 }
   340 
   341 gboolean spawn_sync(char **argv,GError **error)
   342 {
   343     wchar_t *wargv0,**wargv;
   344     gintptr rc;
   345     GError *tmp_error=NULL;
   346     wargv0=g_utf8_to_utf16(argv[0],-1,NULL,NULL,&tmp_error);
   347     if (!wargv0)
   348     {
   349 	fprintf(stderr,"Conversion error in post\n");
   350 	g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
   351 	  "Invalid program name: %s",tmp_error->message);
   352 	g_error_free(tmp_error);
   353 	return FALSE;
   354     }
   355     wargv=win32_argv_import(argv,error);
   356     if (!wargv)
   357     {
   358 	fprintf(stderr,"Conversion error in post\n");
   359 	g_free(wargv0);
   360 	return FALSE;
   361     }
   362     errno=0;
   363     rc=_wspawnvp(P_WAIT,wargv0,(const wchar_t **)wargv);
   364     g_free(wargv0);
   365     g_strfreev((gchar **)wargv);
   366     if (rc==-1 && errno!=0)
   367     {
   368 	fprintf(stderr,"Failed to start post command (%s)\n",g_strerror(errno));
   369 	g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
   370 	  "Failed to execute post command: %s",g_strerror(errno));
   371 	return FALSE;
   372     }
   373     if (rc!=EXIT_SUCCESS)
   374     {
   375 	fprintf(stderr,"post command failed (%ld)\n",(long)rc);
   376 	g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
   377 	  "Post command exited with code %ld",(long)rc);
   378 	return FALSE;
   379     }
   380     fprintf(stderr,"post command exited normally\n");
   381     return TRUE;
   382 }
   383 
   384 #endif /* defined(WIN32) && !defined(USE_G_SPAWN) */
   385 
   386 gboolean pre_install_spawn_sync(char **argv,GError **error)
   387 {
   388     GError *tmp_error=NULL;
   389 #ifdef USE_G_SPAWN
   390     gchar *standard_output,*standard_error;
   391     int exit_status;
   392     if (!g_spawn_sync(NULL,argv,NULL,G_SPAWN_SEARCH_PATH,NULL,NULL,
   393       &standard_output,&standard_error,&exit_status,&tmp_error))
   394     {
   395 	fprintf(stderr,"Failed to start post command\n");
   396 	g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[0]);
   397 	return FALSE;
   398     }
   399     if (standard_output && *standard_output)
   400     {
   401 	printf("Output from post command %s:\n",argv[0]);
   402 	fputs(standard_output,stdout);
   403     }
   404     g_free(standard_output);
   405     if (standard_error && *standard_error)
   406     {
   407 	printf("Error output from post command %s:\n",argv[0]);
   408 	fputs(standard_error,stdout);
   409     }
   410     g_free(standard_error);
   411     if (!g_spawn_check_exit_status(exit_status,&tmp_error))
   412     {
   413 	fprintf(stderr,"post command failed\n");
   414 	g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[0]);
   415 	return FALSE;
   416     }
   417 #else
   418     if (!spawn_sync(argv,&tmp_error))
   419     {
   420 	g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[0]);
   421 	return FALSE;
   422     }
   423 #endif
   424     return TRUE;
   425 }
   426 
   427 print_arguments(int argc,char **argv)
   428 {
   429     int i,j;
   430     for(i=0;i<argc;i++)
   431     {
   432 	if (i)
   433 	    putchar(' ');
   434 	if (strchr(argv[i],' ') || strchr(argv[i],'"') || strchr(argv[i],'\n'))
   435 	{
   436 	    putchar('"');
   437 	    for(j=0;argv[i][j];j++)
   438 	    {
   439 		if (strchr("$`\"\\\n",argv[i][j]))
   440 		    putchar('\\');
   441 		putchar(argv[i][j]);
   442 	    }
   443 	    putchar('"');
   444 	}
   445 	else
   446 	    fputs(argv[i],stdout);
   447     }
   448 }
   449 
   450 /*
   451  * Run a command after completing request.
   452  *
   453  * Command may refer to %INSTALL_PREFIX% which will be replaced by the
   454  * (first) install prefix used, by %TEST_RESULT% which will be replaced
   455  * by either "pass" or "fail" depending as to whether the request succeeded
   456  * or not and/or by %REPOSITORY% which will be replaced by the URI of the
   457  * repository used. Command may also include double quotes which will be used
   458  * to affect how the command is split into arguments much like a shell does.
   459  */
   460 gboolean run_post(int argc,char **argv,gboolean test_result,
   461   const char *repository,GError **error)
   462 {
   463     int i,post_argc;
   464     char *s;
   465     gchar *expanded;
   466     gchar **post_argv;
   467     GError *tmp_error=NULL;
   468     if (argc<2)
   469     {
   470 	g_set_error_literal(error,G_FILE_ERROR,G_FILE_ERROR_NOENT,
   471 	  "--post: No command given");
   472 	return FALSE;
   473     }
   474     if (!g_shell_parse_argv(argv[1],&post_argc,&post_argv,&tmp_error))
   475     {
   476 	g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[1]);
   477 	return FALSE;
   478     }
   479     for(i=0;i<post_argc;i++)
   480     {
   481 	s=strstr(post_argv[i],"%INSTALL_PREFIX%");
   482 	if (s)
   483 	{
   484 	    *s='\0';
   485 	    s+=strlen("%INSTALL_PREFIX%");
   486 	    expanded=g_strconcat(post_argv[i],prefix,s,NULL);
   487 	    g_free(post_argv[i]);
   488 	    post_argv[i]=expanded;
   489 	}
   490 	s=strstr(post_argv[i],"%TEST_RESULT%");
   491 	if (s)
   492 	{
   493 	    *s='\0';
   494 	    s+=strlen("%TEST_RESULT%");
   495 	    expanded=g_strconcat(post_argv[i],test_result?"pass":"fail",s,NULL);
   496 	    g_free(post_argv[i]);
   497 	    post_argv[i]=expanded;
   498 	}
   499 	s=strstr(post_argv[i],"%REPOSITORY%");
   500 	if (s)
   501 	{
   502 	    *s='\0';
   503 	    s+=strlen("%REPOSITORY%");
   504 	    expanded=g_strconcat(post_argv[i],repository,s,NULL);
   505 	    g_free(post_argv[i]);
   506 	    post_argv[i]=expanded;
   507 	}
   508     }
   509     printf("Running post command: ");
   510     print_arguments(post_argc,post_argv);
   511     putchar('\n');
   512     return pre_install_spawn_sync(post_argv,error);
   513 }
   514 
   515 #ifdef WIN32
   516 DWORD win32_pre_install_gui(char *repository)
   517 {
   518     HANDLE thread;
   519     INITCOMMONCONTROLSEX icc={0,};
   520     MSG msg;
   521     DWORD retval;
   522     main_thread_id=GetCurrentThreadId();
   523     thread=(HANDLE)pre_install(repository);
   524     if (!thread)
   525 	return EXIT_FAILURE;
   526     icc.dwSize=sizeof(icc);
   527     icc.dwICC=ICC_WIN95_CLASSES;
   528     InitCommonControlsEx(&icc);
   529     DialogBox(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_PROGRESSDIALOG),NULL,
   530       &ProgressDialogProc);
   531     while(GetMessage(&msg,NULL,0,0)>0)
   532     {
   533 	TranslateMessage(&msg);
   534 	DispatchMessage(&msg);
   535     }
   536     WaitForSingleObject(thread,INFINITE);
   537     GetExitCodeThread(thread,&retval);
   538     CloseHandle(thread);
   539     return retval;
   540 }
   541 #endif	/* WIN32 */
   542 
   543 gchar *pre_install_default_repository(const char *argv0)
   544 {
   545     size_t length;
   546     void *contents;
   547     gchar *path;
   548     gchar *s,*uri;
   549     struct razor_error *tmp_error=NULL;
   550     /*
   551      * The default repository is the executable itself if it's an archive
   552      * or otherwise the directory in which the executable is stored.
   553      */
   554     path=plover_get_program(argv0);
   555     uri=razor_path_to_uri(path);
   556     free(path);
   557     s=g_strconcat(uri,"/repodata/comps.xml",NULL);
   558     contents=razor_uri_get_contents(s,&length,FALSE,&tmp_error);
   559     g_free(s);
   560     if (contents)
   561 	razor_uri_free_contents(contents,length);
   562     else
   563     {
   564 	if (razor_error_matches(tmp_error,RAZOR_GENERAL_ERROR,
   565 	  RAZOR_GENERAL_ERROR_UNSUPPORTED_ARCHIVE))
   566 	{
   567 	    path=plover_get_program_directory(argv0);
   568 	    uri=razor_path_to_uri(path);
   569 	    free(path);
   570 	}
   571 	razor_error_free(tmp_error);
   572     }
   573     return uri;
   574 }
   575 
   576 gboolean pre_install_post(int argc,char **argv,gboolean success,
   577   const char *repository)
   578 {
   579     gchar *s,*uri;
   580     struct post *post=NULL;
   581     GError *error=NULL;
   582     if (!argv && !success)
   583     {
   584 	/*
   585 	 * If no --post command has been given, then we want to run the
   586 	 * program specified in %INSTALL_PREFIX%/etc/pre-inst.post
   587 	 * However, if the pre-install failed, then that file isn't likely
   588 	 * to exist (and it's requirements even less so) so we issue an error
   589 	 * instead. Unfortunately, it's hard to include the error message
   590 	 * here. It will have been logged in the plover log, but that's not
   591 	 * much help to users.
   592 	 */
   593 #ifndef WIN32
   594 	fprintf(stderr,
   595 	  "Installation failed: Failed to unpack the main installer\n");
   596 #else
   597 	MessageBox(NULL,"Failed to unpack the main installer",
   598 	  "Installation failed",MB_ICONERROR|MB_OK);
   599 #endif
   600 	return FALSE;
   601     }
   602     if (!argv)
   603     {
   604 	post=pre_install_post_new(repository,prefix);
   605 	uri=razor_path_to_uri(prefix);
   606 	s=g_strconcat(uri,"/etc/pre-inst.post",NULL);
   607 	g_free(uri);
   608 	pre_install_post_load_uri(post,s,&error);
   609 	if (error)
   610 	    g_debug("Failed to load post configuration from pre-inst.post: %s",
   611 	      error->message);
   612 	g_free(s);
   613 	if (g_error_matches(error,PLOVER_POSIX_ERROR,ENOENT))
   614 	{
   615 	    /*
   616 	     * If no post configuration file exists, that's not an error;
   617 	     * there simply is no post action configured.
   618 	     */
   619 	    g_clear_error(&error);
   620 	}
   621     }
   622     if (post && post->argc>0)
   623     {
   624 	printf("Running post command: ");
   625 	print_arguments(post->argc,post->argv);
   626 	putchar('\n');
   627 	pre_install_spawn_sync(post->argv,&error);
   628     }
   629     else if (argv)
   630 	run_post(argc,argv,success,repository,&error);
   631     if (post)
   632 	pre_install_post_free(post);
   633     if (error)
   634     {
   635 #ifndef WIN32
   636 	fprintf(stderr,"Error in post: %s\n",error->message);
   637 #else
   638 	MessageBox(NULL,error->message,"Error in post",MB_ICONERROR|MB_OK);
   639 #endif
   640 	g_error_free(error);
   641 	success=FALSE;
   642     }
   643     return success;
   644 }
   645 
   646 int main(int argc,char **argv)
   647 {
   648     gboolean success,uninstall=FALSE,enable_post=FALSE;
   649     GError *error=NULL;
   650     gchar *path=NULL,*repository=NULL;
   651     GOptionContext *context;
   652     GOptionEntry options[]={
   653 	{"repository",0,0,G_OPTION_ARG_STRING,&repository,
   654 	  "Repository location","uri"},
   655 	{"path",0,0,G_OPTION_ARG_FILENAME,&path,
   656 	  "Repository path","path"},
   657 	{"uninstall",'u',0,G_OPTION_ARG_NONE,&uninstall,
   658 	  "Uninstall all packages",NULL},
   659 	{"post",0,0,G_OPTION_ARG_NONE,&enable_post,
   660 	  "Run command after request is processed",NULL},
   661 	{NULL}
   662     };
   663 #ifdef WIN32
   664     /*
   665      * pre-inst is normally a GUI application, but rpm scripts may well
   666      * call console applications and it looks ugly if console windows keep
   667      * popping up. Avoid this by allocating our own console and hiding it.
   668      * Note:
   669      *	- If pre-inst is a console application (typically for debugging),
   670      *    then skip this step.
   671      *  - Call ShowWindow twice to negate special handling on first call.
   672      */
   673     if (!GetConsoleWindow())
   674     {
   675 	AllocConsole();
   676 	ShowWindow(GetConsoleWindow(),SW_HIDE);
   677 	ShowWindow(GetConsoleWindow(),SW_HIDE);
   678     }
   679 #endif
   680     plover_exception_handler_init();
   681     context=g_option_context_new("[command] - install the main installer");
   682     g_option_context_add_main_entries(context,options,NULL);
   683     g_option_context_set_description(context,
   684       "If --post is specified, then the command to run and its arguments\n"
   685       "should be listed at the end of the command line.\n"
   686       "Command may refer to %INSTALL_PREFIX% which will be replaced by the\n"
   687       "(first) install prefix used, by %TEST_RESULT% which will be\n"
   688       "replaced by either \"pass\" or \"fail\" depending as to whether the\n"
   689       "request succeeded or not and/or by %REPOSITORY% which will be\n"
   690       "replaced by the URI of the repository used. Command may also include\n"
   691       "double quotes which will be used to affect how the command is split\n"
   692       "into arguments much like a shell does.");
   693     g_option_context_set_strict_posix(context,TRUE);
   694     g_option_context_set_ignore_unknown_options(context,TRUE);
   695     if (!g_option_context_parse(context,&argc,&argv,&error))
   696     {
   697 	g_printerr("pre-install: %s\n",error->message);
   698 	g_printerr("Use \"%s --help\" for help\n",argv[0]);
   699 	exit(1);
   700     }
   701     if (repository && path)
   702     {
   703 	g_printerr("pre-install: "
   704 	  "Only one of --repository and --path can be specified\n");
   705 	exit(1);
   706     }
   707     if (path)
   708 	repository=razor_path_to_uri(path);
   709     else if (!repository)
   710 	repository=pre_install_default_repository(argv[0]);
   711     if (uninstall)
   712 	success=pre_uninstall();
   713     else
   714     {
   715 #ifdef WIN32
   716 	success=win32_pre_install_gui(repository)==EXIT_SUCCESS;
   717 #else
   718 	success=!!pre_install(repository);
   719 #endif
   720     }
   721     if (enable_post)
   722 	success=pre_install_post(argc,argv,success,repository);
   723     else if (!uninstall)
   724 	success=pre_install_post(0,NULL,success,repository);
   725 #ifdef WIN32
   726     return success?EXIT_SUCCESS:EXIT_FAILURE;
   727 #else
   728     return success?0:1;
   729 #endif
   730 }