Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!

Tables.c

By: Need4Sleep on Dec 4th, 2012  |  syntax: C++  |  size: 16.02 KB  |  views: 32  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. /*                         T A B L E S . C
  2.  * BRL-CAD
  3.  *
  4.  * Copyright (c) 2008-2012 United States Government as represented by
  5.  * the U.S. Army Research Laboratory.
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public License
  9.  * version 2.1 as published by the Free Software Foundation.
  10.  *
  11.  * This library is distributed in the hope that it will be useful, but
  12.  * WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14.  * Lesser General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU Lesser General Public
  17.  * License along with this file; see the file named COPYING for more
  18.  * information.
  19.  */
  20. /** @file libged/tables.c
  21.  *
  22.  * The tables command.
  23.  *
  24.  */
  25.  
  26. #include "common.h"
  27.  
  28. #include <stdlib.h>
  29. #ifdef HAVE_SYS_TYPES_H
  30. #  include <sys/types.h>
  31. #endif
  32. #ifdef HAVE_PWD_H
  33. #  include <pwd.h>
  34. #endif
  35. #include <ctype.h>
  36. #include <string.h>
  37. #include "bio.h"
  38.  
  39. #include "./ged_private.h"
  40. #define BUFF 1024
  41. #define MAX_RESULT_LEN 1024 ///ADDED
  42. void
  43. GetStr(char *ix, char* Result)  //call to get second number in function
  44. {
  45.     char *spacing;              //iterator to traverse
  46.     spacing = ix;               //iterator = pos of ix
  47.     int LEN = 0;                //length and offset
  48.     int Offset = 0;
  49.  
  50.     while(*spacing != ' ')      //while not at end of first num
  51.     {
  52.         Offset++;               //offset is more
  53.         spacing++;
  54.     }
  55.     spacing++;                  //go one ahead of the space
  56.     Offset++;
  57.  
  58.     while(*spacing != ' ')      //while not end of second number
  59.     {
  60.         spacing++;
  61.         Offset++;
  62.         LEN++;                  //length of number
  63.     }
  64.  
  65.     if (LEN > MAX_RESULT_LEN - 1) {
  66.         LEN = MAX_RESULT_LEN - 1;
  67.     }
  68.  
  69.     strncpy(Result, ix + (Offset - LEN),LEN);
  70.     Result[LEN] = '\0';
  71. }
  72.  
  73. int
  74. sort(const void* a, const void* b)
  75. {
  76.     char *ia = *(char**)a;
  77.     char *ib = *(char**)b;
  78.  
  79.     char Str[MAX_RESULT_LEN];
  80.     char Str2[MAX_RESULT_LEN];
  81.  
  82.     GetStr(ia, Str);
  83.     GetStr(ib, Str2);
  84.  
  85.     int n1 = atoi(Str);
  86.     int n2 = atoi(Str2);
  87.     return (n2 - n1);
  88. }
  89.  
  90.  
  91.  
  92. /* structure to distinguish new solids from existing (old) solids */
  93. struct identt {
  94.     int i_index;
  95.     char i_name[NAMESIZE+1];
  96.     mat_t i_mat;
  97. };
  98.  
  99.  
  100. #define ABORTED         -99
  101. #define OLDSOLID        0
  102. #define NEWSOLID        1
  103. #define SOL_TABLE       1
  104. #define REG_TABLE       2
  105. #define ID_TABLE        3
  106.  
  107. static int idfd = 0;
  108. static int rd_idfd = 0;
  109. static FILE *tabptr = NULL;
  110.  
  111. HIDDEN int
  112. tables_check(char *a, char *b)
  113. {
  114.  
  115.     int c= sizeof(struct identt);
  116.  
  117.     while (c--) if (*a++ != *b++) return 0;     /* no match */
  118.     return 1;   /* match */
  119.  
  120. }
  121.  
  122.  
  123. HIDDEN int
  124. tables_sol_number(const matp_t matrix, char *name, int *old, long *numsol)
  125. {
  126.     int i;
  127.     struct identt idbuf1, idbuf2;
  128.     static struct identt identt = {0, {0}, MAT_INIT_ZERO};
  129.     int readval;
  130.  
  131.     memset(&idbuf1, 0, sizeof(struct identt));
  132.     bu_strlcpy(idbuf1.i_name, name, sizeof(idbuf1.i_name));
  133.     MAT_COPY(idbuf1.i_mat, matrix);
  134.  
  135.     for (i = 0; i < *numsol; i++) {
  136.         (void)lseek(rd_idfd, i*(long)sizeof identt, 0);
  137.         readval = read(rd_idfd, &idbuf2, sizeof identt);
  138.  
  139.         if (readval < 0) {
  140.             perror("READ ERROR");
  141.         }
  142.  
  143.         idbuf1.i_index = i + 1;
  144.  
  145.         if (tables_check((char *)&idbuf1, (char *)&idbuf2) == 1) {
  146.             *old = 1;
  147.             return idbuf2.i_index;
  148.         }
  149.     }
  150.     (*numsol)++;
  151.     idbuf1.i_index = *numsol;
  152.  
  153.     (void)lseek(idfd, (off_t)0L, 2);
  154.     i = write(idfd, &idbuf1, sizeof identt);
  155.     if (i < 0)
  156.         perror("write");
  157.  
  158.     *old = 0;
  159.     return idbuf1.i_index;
  160. }
  161.  
  162.  
  163. HIDDEN void
  164. tables_new(struct ged *gedp, struct directory *dp, struct bu_ptbl *cur_path, const fastf_t *old_mat, int flag, long *numreg, long *numsol)
  165. {
  166.     struct rt_db_internal intern;
  167.     struct rt_comb_internal *comb;
  168.     struct rt_tree_array *tree_list;
  169.     size_t node_count;
  170.     size_t actual_count;
  171.     size_t i, k;
  172.  
  173.     RT_CK_DIR(dp);
  174.     BU_CK_PTBL(cur_path);
  175.  
  176.     if (!(dp->d_flags & RT_DIR_COMB))
  177.         return;
  178.  
  179.     if (rt_db_get_internal(&intern, dp, gedp->ged_wdbp->dbip, (fastf_t *)NULL, &rt_uniresource) < 0) {
  180.         bu_vls_printf(gedp->ged_result_str, "Database read error, aborting\n");
  181.         return;
  182.     }
  183.  
  184.     comb = (struct rt_comb_internal *)intern.idb_ptr;
  185.     RT_CK_COMB(comb);
  186.  
  187.     if (comb->tree && db_ck_v4gift_tree(comb->tree) < 0) {
  188.         db_non_union_push(comb->tree, &rt_uniresource);
  189.         if (db_ck_v4gift_tree(comb->tree) < 0) {
  190.             bu_vls_printf(gedp->ged_result_str, "Cannot flatten tree for editing\n");
  191.             intern.idb_meth->ft_ifree(&intern);
  192.             return;
  193.         }
  194.     }
  195.  
  196.     if (!comb->tree) {
  197.         /* empty combination */
  198.         intern.idb_meth->ft_ifree(&intern);
  199.         return;
  200.     }
  201.  
  202.     node_count = db_tree_nleaves(comb->tree);
  203.     tree_list = (struct rt_tree_array *)bu_calloc(node_count,
  204.                                                   sizeof(struct rt_tree_array), "tree list");
  205.  
  206.     /* flatten tree */
  207.     actual_count = (struct rt_tree_array *)db_flatten_tree(tree_list,
  208.                                                            comb->tree, OP_UNION, 0, &rt_uniresource) - tree_list;
  209.     BU_ASSERT_SIZE_T(actual_count, ==, node_count);
  210.  
  211.     if (dp->d_flags & RT_DIR_REGION) {
  212.         (*numreg)++;
  213.         (void)fprintf(tabptr, " %-4ld %4ld %4ld %4ld %4ld  ",
  214.                       *numreg, comb->region_id, comb->aircode, comb->GIFTmater,
  215.                       comb->los);
  216.         for (k = 0; k < BU_PTBL_LEN(cur_path); k++) {
  217.             struct directory *path_dp;
  218.  
  219.             path_dp = (struct directory *)BU_PTBL_GET(cur_path, k);
  220.             RT_CK_DIR(path_dp);
  221.             (void)fprintf(tabptr, "/%s", path_dp->d_namep);
  222.         }
  223.         (void)fprintf(tabptr, "/%s:\n", dp->d_namep);
  224.  
  225.         if (flag == ID_TABLE)
  226.             goto out;
  227.  
  228.         for (i = 0; i < actual_count; i++) {
  229.             char op;
  230.             int nsoltemp=0;
  231.             struct rt_db_internal sol_intern;
  232.             struct directory *sol_dp;
  233.             mat_t temp_mat;
  234.             int old;
  235.  
  236.             switch (tree_list[i].tl_op) {
  237.                 case OP_UNION:
  238.                     op = 'u';
  239.                     break;
  240.                 case OP_SUBTRACT:
  241.                     op = '-';
  242.                     break;
  243.                 case OP_INTERSECT:
  244.                     op = '+';
  245.                     break;
  246.                 default:
  247.                     bu_log("unrecognized operation in region %s\n", dp->d_namep);
  248.                     op = '?';
  249.                     break;
  250.             }
  251.  
  252.             if ((sol_dp=db_lookup(gedp->ged_wdbp->dbip, tree_list[i].tl_tree->tr_l.tl_name, LOOKUP_QUIET)) != RT_DIR_NULL) {
  253.                 if (sol_dp->d_flags & RT_DIR_COMB) {
  254.                     (void)fprintf(tabptr, "   RG %c %s\n",
  255.                                   op, sol_dp->d_namep);
  256.                     continue;
  257.                 } else if (!(sol_dp->d_flags & RT_DIR_SOLID)) {
  258.                     (void)fprintf(tabptr, "   ?? %c %s\n",
  259.                                   op, sol_dp->d_namep);
  260.                     continue;
  261.                 } else {
  262.                     if (tree_list[i].tl_tree->tr_l.tl_mat) {
  263.                         bn_mat_mul(temp_mat, old_mat,
  264.                                    tree_list[i].tl_tree->tr_l.tl_mat);
  265.                     } else {
  266.                         MAT_COPY(temp_mat, old_mat);
  267.                     }
  268.                     if (rt_db_get_internal(&sol_intern, sol_dp, gedp->ged_wdbp->dbip, temp_mat, &rt_uniresource) < 0) {
  269.                         bu_log("Could not import %s\n", tree_list[i].tl_tree->tr_l.tl_name);
  270.                         nsoltemp = 0;
  271.                     }
  272.                     nsoltemp = tables_sol_number((matp_t)temp_mat, tree_list[i].tl_tree->tr_l.tl_name, &old, numsol);
  273.                     (void)fprintf(tabptr, "   %c [%d] ", op, nsoltemp);
  274.                 }
  275.             } else {
  276.                 nsoltemp = tables_sol_number((matp_t)old_mat, tree_list[i].tl_tree->tr_l.tl_name, &old, numsol);
  277.                 (void)fprintf(tabptr, "   %c [%d] ", op, nsoltemp);
  278.                 continue;
  279.             }
  280.  
  281.             if (flag == REG_TABLE || old) {
  282.                 (void) fprintf(tabptr, "%s\n", tree_list[i].tl_tree->tr_l.tl_name);
  283.                 continue;
  284.             } else
  285.                 (void) fprintf(tabptr, "%s:  ", tree_list[i].tl_tree->tr_l.tl_name);
  286.  
  287.             if (!old && (sol_dp->d_flags & RT_DIR_SOLID)) {
  288.                 /* if we get here, we must be looking for a solid table */
  289.                 struct bu_vls tmp_vls = BU_VLS_INIT_ZERO;
  290.  
  291.                 if (!rt_functab[sol_intern.idb_type].ft_describe ||
  292.                     rt_functab[sol_intern.idb_type].ft_describe(&tmp_vls, &sol_intern, 1, gedp->ged_wdbp->dbip->dbi_base2local, &rt_uniresource, gedp->ged_wdbp->dbip) < 0) {
  293.                     bu_vls_printf(gedp->ged_result_str, "%s describe error\n", tree_list[i].tl_tree->tr_l.tl_name);
  294.                 }
  295.                 fprintf(tabptr, "%s", bu_vls_addr(&tmp_vls));
  296.                 bu_vls_free(&tmp_vls);
  297.             }
  298.             if (nsoltemp && (sol_dp->d_flags & RT_DIR_SOLID))
  299.                 rt_db_free_internal(&sol_intern);
  300.         }
  301.     } else if (dp->d_flags & RT_DIR_COMB) {
  302.         int cur_length;
  303.  
  304.         bu_ptbl_ins(cur_path, (long *)dp);
  305.         cur_length = BU_PTBL_END(cur_path);
  306.  
  307.         for (i = 0; i < actual_count; i++) {
  308.             struct directory *nextdp;
  309.             mat_t new_mat;
  310.  
  311.             nextdp = db_lookup(gedp->ged_wdbp->dbip, tree_list[i].tl_tree->tr_l.tl_name, LOOKUP_NOISY);
  312.             if (nextdp == RT_DIR_NULL) {
  313.                 bu_vls_printf(gedp->ged_result_str, "\tskipping this object\n");
  314.                 continue;
  315.             }
  316.  
  317.             /* recurse */
  318.             if (tree_list[i].tl_tree->tr_l.tl_mat) {
  319.                 bn_mat_mul(new_mat, old_mat, tree_list[i].tl_tree->tr_l.tl_mat);
  320.             } else {
  321.                 MAT_COPY(new_mat, old_mat);
  322.             }
  323.             tables_new(gedp, nextdp, cur_path, new_mat, flag, numreg, numsol);
  324.             bu_ptbl_trunc(cur_path, cur_length);
  325.         }
  326.     } else {
  327.         bu_vls_printf(gedp->ged_result_str, "Illegal flags for %s skipping\n", dp->d_namep);
  328.         return;
  329.     }
  330.  
  331. out:
  332.     bu_free((char *)tree_list, "new_tables: tree_list");
  333.     intern.idb_meth->ft_ifree(&intern);
  334.     return;
  335. }
  336.  
  337.  
  338. int
  339. ged_tables(struct ged *gedp, int argc, const char *argv[])
  340. {
  341.     //static const char sortcmd_orig[] = "sort -n +1 -2 -o /tmp/ord_id ";
  342.     //static const char sortcmd_long[] = "sort --numeric --key=2, 2 --output /tmp/ord_id ";
  343.     static const char catcmd[] = "cat /tmp/ord_id >> ";
  344.     struct bu_vls tmp_vls = BU_VLS_INIT_ZERO;
  345.     struct bu_vls cmd = BU_VLS_INIT_ZERO;
  346.     struct bu_ptbl cur_path;
  347.     char * TempStr;
  348.     int flag;
  349.     int status;
  350.     char *timep;
  351.     time_t now;
  352.     int i;
  353.     int LineLength;
  354.     char ** DataArray;
  355.     const char *usage = "file object(s)";
  356.     FILE * TMP = NULL;
  357.     FILE *tabprtSrt = NULL;
  358.  
  359.     long int numreg = 0;
  360.     long int numsol = 0;
  361.  
  362.     GED_CHECK_DATABASE_OPEN(gedp, GED_ERROR);
  363.     GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
  364.  
  365.     /* initialize result */
  366.     bu_vls_trunc(gedp->ged_result_str, 0);
  367.  
  368.     /* must be wanting help */
  369.     if (argc == 1) {
  370.         bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
  371.         return GED_HELP;
  372.     }
  373.  
  374.     if (argc < 3) {
  375.         bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
  376.         return GED_ERROR;
  377.     }
  378.  
  379.     bu_ptbl_init(&cur_path, 8, "f_tables: cur_path");
  380.  
  381.     status = GED_OK;
  382.  
  383.     /* find out which ascii table is desired */
  384.     if (BU_STR_EQUAL(argv[0], "solids")) {
  385.         /* complete summary - down to solids/paremeters */
  386.         flag = SOL_TABLE;
  387.     } else if (BU_STR_EQUAL(argv[0], "regions")) {
  388.         /* summary down to solids as members of regions */
  389.         flag = REG_TABLE;
  390.     } else if (BU_STR_EQUAL(argv[0], "idents")) {
  391.         /* summary down to regions */
  392.         flag = ID_TABLE;
  393.     } else {
  394.         /* should never reach here */
  395.         bu_vls_printf(gedp->ged_result_str, "%s:  input error\n", argv[0]);
  396.         status = GED_ERROR;
  397.         goto end;
  398.     }
  399.  
  400.     /* open the file */
  401.     if ((tabptr=fopen(argv[1], "w+")) == NULL) {
  402.         bu_vls_printf(gedp->ged_result_str, "%s:  Can't open %s\n", argv[0], argv[1]);
  403.         status = GED_ERROR;
  404.         goto end;
  405.     }
  406.  
  407.     if (flag == SOL_TABLE || flag == REG_TABLE) {
  408.         /* temp file for discrimination of solids */
  409.         /* !!! this needs to be a bu_temp_file() */
  410.         if ((idfd = creat("/tmp/mged_discr", 0600)) < 0) {
  411.             perror("/tmp/mged_discr");
  412.             status = GED_ERROR;
  413.             goto end;
  414.         }
  415.         rd_idfd = open("/tmp/mged_discr", 2);
  416.     }
  417.  
  418.     (void)time(&now);
  419.     timep = ctime(&now);
  420.     timep[24] = '\0';
  421.     (void)fprintf(tabptr, "1 -8    Summary Table {%s}  (written: %s)\n", argv[0], timep);
  422.     (void)fprintf(tabptr, "2 -7         file name    : %s\n", gedp->ged_wdbp->dbip->dbi_filename);
  423.     (void)fprintf(tabptr, "3 -6         \n");
  424.     (void)fprintf(tabptr, "4 -5         \n");
  425. #ifndef _WIN32
  426.     (void)fprintf(tabptr, "5 -4         user         : %s\n", getpwuid(getuid())->pw_gecos);
  427. #else
  428.     {
  429.         char uname[256];
  430.         DWORD dwNumBytes = 256;
  431.         if (GetUserName(uname, &dwNumBytes))
  432.             (void)fprintf(tabptr, "5 -4         user         : %s\n", uname);
  433.         else
  434.             (void)fprintf(tabptr, "5 -4         user         : UNKNOWN\n");
  435.     }
  436. #endif
  437.     (void)fprintf(tabptr, "6 -3         target title : %s\n", gedp->ged_wdbp->dbip->dbi_title);
  438.     (void)fprintf(tabptr, "7 -2         target units : %s\n",
  439.                   bu_units_string(gedp->ged_wdbp->dbip->dbi_local2base));
  440.     (void)fprintf(tabptr, "8 -1         objects      :");
  441.     for (i = 2; i < argc; i++) {
  442.         if ((i%8) == 0)
  443.             (void)fprintf(tabptr, "\n                           ");
  444.         (void)fprintf(tabptr, " %s", argv[i]);
  445.     }
  446.     (void)fprintf(tabptr, "\n\n");
  447.  
  448.     /* make the tables */
  449.     for (i = 2; i < argc; i++) {
  450.         struct directory *dp;
  451.  
  452.         bu_ptbl_reset(&cur_path);
  453.         if ((dp = db_lookup(gedp->ged_wdbp->dbip, argv[i], LOOKUP_NOISY)) != RT_DIR_NULL)
  454.             tables_new(gedp, dp, &cur_path, (const fastf_t *)bn_mat_identity, flag, &numreg, &numsol);
  455.         else
  456.             bu_vls_printf(gedp->ged_result_str, "%s:  skip this object\n", argv[i]);
  457.     }
  458.  
  459.  
  460.     bu_vls_printf(gedp->ged_result_str, "Summary written in: %s\n", argv[1]);
  461.  
  462.     if (flag == SOL_TABLE || flag == REG_TABLE) {
  463.         bu_file_delete("/tmp/mged_discr\0");
  464.         (void)fprintf(tabptr, "\n\nNumber Primitives = %ld  Number Regions = %ld\n",
  465.                       numsol, numreg);
  466.  
  467.         bu_vls_printf(gedp->ged_result_str, "Processed %d Primitives and %d Regions\n",
  468.                       numsol, numreg);
  469.  
  470.         (void)fclose(tabptr);
  471.     } else {
  472.         int ret;
  473.  
  474.         (void)fprintf(tabptr, "* 9999999\n* 9999999\n* 9999999\n* 9999999\n* 9999999\n");
  475.         (void)fclose(tabptr);
  476.  
  477.  
  478.     tabprtSrt = fopen(argv[1],"r");     //open file to read
  479.     TempStr = (char*)malloc(BUFF);      //allocate memory for string
  480.                                         //while line grab from file is successful
  481.     while(fgets(TempStr, sizeof(TempStr), tabprtSrt) != NULL)
  482.     {
  483.         LineLength++;
  484.     }
  485.  
  486.                                         //allocate string to hold data
  487.     DataArray = (char**)malloc(LineLength * sizeof(char*));
  488.     int g; for(g = 0; g < LineLength; g++)
  489.     {
  490.         DataArray[g] = (char*)malloc(BUFF);
  491.     }
  492.  
  493.     int Count = 0;                      //replicate for loop, provide counter
  494.     while(fgets(TempStr, sizeof(TempStr), tabprtSrt) != NULL)
  495.     {
  496.         strcpy(DataArray[Count],TempStr); //copy data from file
  497.         Count++;                        //increment counter
  498.     }
  499.     fclose(tabprtSrt);                  //close file used
  500.     free(TempStr);                      //free memory
  501.  
  502.  
  503.  
  504.                                         //call sort on array
  505.         qsort(DataArray, LineLength, sizeof(char *),sort);
  506.  
  507.  
  508.  
  509.         TMP = fopen("/tmp/ord_id","w+");    //open temp file to write to
  510.         int k;
  511.         for(k = 0; k < LineLength; k++)     //write contents of sorted array to file
  512.         {
  513.             fprintf(TMP,"%s\n",DataArray[k]);
  514.         }
  515.     fclose(TMP);                        //close file
  516.         bu_vls_printf(gedp->ged_result_str, "Processed %d Regions\n", numreg);
  517.  
  518.         /* make ordered idents - tries newer gnu 'sort' syntax if not successful */
  519.         //bu_vls_strcpy(&cmd, sortcmd_orig);
  520.         //bu_vls_strcat(&cmd, argv[1]);
  521.         //bu_vls_strcat(&cmd, " 2> /dev/null");
  522.         //if (system(bu_vls_addr(&cmd)) != 0) {
  523.             //bu_vls_trunc(&cmd, 0);
  524.             //bu_vls_strcpy(&cmd, sortcmd_long);
  525.             //bu_vls_strcat(&cmd, argv[1]);
  526.             //ret = system(bu_vls_addr(&cmd));
  527.             //if (ret != 0)
  528.                 //bu_log("WARNING: sort failure detected\n");
  529.         bu_vls_printf(gedp->ged_result_str, "%V\n", &cmd);
  530.  
  531.         bu_vls_trunc(&cmd, 0);
  532.         bu_vls_strcpy(&cmd, catcmd);
  533.         bu_vls_strcat(&cmd, argv[1]);
  534.         bu_vls_printf(gedp->ged_result_str, "%V\n", &cmd);
  535.         ret = system(bu_vls_addr(&cmd));
  536.         if (ret != 0)
  537.             bu_log("WARNING: cat failure detected\n");
  538.  
  539.     int h; for(h = 0; h < LineLength; h++)  //free memory used by array
  540.         free(ArrayData[h]);
  541.     free(ArrayData);
  542.         bu_file_delete("/tmp/ord_id\0");
  543.     }
  544.  
  545. end:
  546.     bu_vls_free(&cmd);
  547.     bu_vls_free(&tmp_vls);
  548.     bu_ptbl_free(&cur_path);
  549.  
  550.     return status;
  551. }
  552.  
  553.  
  554. /*
  555.  * Local Variables:
  556.  * tab-width: 8
  557.  * mode: C
  558.  * indent-tabs-mode: t
  559.  * c-file-style: "stroustrup"
  560.  * End:
  561.  * ex: shiftwidth=4 tabstop=8
  562.  */
clone this paste RAW Paste Data